I have a problem with classes. I am using https://www.php-fig.org/psr/psr-4/ for autoload classes.
------------------------------------ /home/classes/a/casea.php
namespace Classes\A\CaseA;
class ClassA {
public function MethodA()
{
$resultA = 'Method A works!';
return $resultA;
}
}
------------------------------------ /home/classes/b/caseb.php
namespace Classes\B\CaseB;
class ClassB {
public function MethodB()
{
// Classes\A\CaseA\ClassA MethodA() don't work here.
// I tried this, but didn't work.
$obj = new Classes\A\CaseA\ClassA;
$result = $obj->MehodA();
return $result;
}
}
$classb = new Classes\B\CaseB\ClassB;
$show = $classb->MethodB();
echo $show;
------------------------------------ Autoload
## PHP-FIG Autoload
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Classes\\';
// base directory for the namespace prefix
$base_dir = PATH . '/classes/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . strtolower(str_replace('\\', '/', $relative_class)) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
Error is the following text.
Fatal error: Uncaught Error: Class 'Classes\B\Classes\A\ClassA' not found in /home/domain.tld/classes/b/classb.php:11 Stack trace: #0
I solve the problem, if anybody need like that, can use following codes.
in caseb.php
namespace Classes\B\CaseB;
use Classes\A\CaseA\ClassA;
class ClassB extends ClassA {
public function MethodB()
{
$result = parent::MehodA();
return $result;
}
}
Related
I encounterd a little problem with my classes : they simply do not load through my autoloader.
I get this message error :
Warning:
require(C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php):
failed to open stream: No such file or directory in
C:\wamp64\www\blog\app\Autoloader.php on line 23
Fatal error: require(): Failed opening required
'C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php'
(include_path='.;C:\php\pear') in
C:\wamp64\www\blog\app\Autoloader.php on line 23
Factory class :
use Core\config;
use Core\Database\MysqlDatabase;
class App {
public $title = "My super site";
private $db_instance;
private static $_instance;
public static function getInstance()
{
if (is_null(self::$_instance))
{
self::$_instance = new App();
}
return self::$_instance;
}
public static function load()
{
session_start();
require ROOT . '/app/Autoloader.php';
App\Autoloader::register();
require ROOT .'/core/Autoloader.php';
Core\Autoloader::register();
}
public function getTable($name)
{
$class_name = '\\App\\Table\\' . ucfirst($name) .'Table';
return new $class_name($this->getDb());
}
public function getDb()
{
$config = Config::getInstance(ROOT . '/config/config.php');
if (is_null($this->db_instance)) {
$this->db_instance = new MysqlDatabase($config->get('db_name'), $config->get('db_user'), $config->get('db_pass'), $config->get('db_host'));
}
return $this->db_instance;
}
}
Namespace App autoloader class :
<?php
namespace App;
class Autoloader {
static function register()
{
spl_autoload_register(array(__CLASS__, 'autoload')); // __CLASS__ load the current class
}
static function autoload($class)
{
if (strpos($class, __NAMESPACE__ .'\\') === 0) {
$class = str_replace(__NAMESPACE__ . '\\', '', $class); // _NAMESPACE_ load the current name_space
$class = str_replace('\\', '/', $class);
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
}
}
}
Namespace Core autoloader class :
<?php
namespace Core;
class Autoloader {
static function register()
{
spl_autoload_register(array(__CLASS__, 'autoload')); // __CLASS__ load the current class
}
static function autoload($class)
{
if (strpos($class, __NAMESPACE__ .'\\') === 0) {
$class = str_replace(__NAMESPACE__ . '\\', '', $class); // _NAMESPACE_ load the current name_space
$class = str_replace('\\', '/', $class);
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
}
}
}
Empty PostTable
namespace App\Table;
use Core\Table\Table;
class PostsTable extends Table
{
}
Index page :
define('ROOT', dirname(__DIR__));
require ROOT . '/app/App.php';
App::load();
$app = App::getInstance();
$posts = $app->getTable('Posts');
var_dump($posts->all());
How to make it works please?
AS I said in the comments check this path
require(C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php)
Doesn't look right to me
require(C:\wamp64\www\blog\ [appAutoloader.php] /Table/PostsTable.php)
What's that bit doing there....
Also namespace of App is not app for the folder its App because this may work on Windows but you will find it does not work on Linux. Because Linux paths are case sensitive, and windows are not.
Further this makes little to no sense
require __DIR__ . 'Autoloader.php/' . $class . '.php'; // __DIR__ = the parent folder. Here "app"
Require 2 files? Paths don't work that way, not that I am aware of at least.
On top of that your implementation ignores the _ Typically underlines will be part of the class name but are replaced by directory, this allows a shorter namespace. So for example instead of having a namespace like this
Namespace \APP\Table;
class PostsTable ..
You could have a class in the same place Like so
Namespace \APP;
class Table_PostsTable ..
With a shorter namespace but still located in the App/Table/PostsTable.php file. However, that's just how I read the spec for PSR autoloaders.
PRO TIP
Take this path C:\wamp64\www\blog\appAutoloader.php/Table/PostsTable.php open the file browser on you desktop and see if it pulls up the file by pasting it into the navigation bar. It wont, but you can be sure your path is wrong by eliminating the code.
It is possible to make accessible a Class within spl_autoload_register (automatically)?
For example, I using spl_autoload_register in index.php:
<?php
class Utils {
public function example() {
echo 'Hello word!';
}
}
spl_autoload_register(function($class)
{
$relative_class = strtolower(str_replace('\\', '/', $class));
$file = './src/' . $relative_class . '.php';
if (is_file($file)) {
require_once $file;
}
});
$user = new \Controllers\Foo\User;
This new \Controllers\Foo\User; autoload this file ./src/controllers/foo/user.php
user.php:
<?php
namespace Controllers/Foo;
class User
{
public function foo() {
//Something...
}
}
If I need to use a Utils class I'll have to add new \Controllers\Foo\User in the file user.php like this:
public function foo() {
\Utils::example();
}
or
<?php
namespace Controllers/Foo;
use \Utils as Utils;
class User
{
public function foo() {
Utils::example();
}
}
It is possible to make accessible to Utils class within spl_autoload_register (automatically)? I would use without use \Utils as Utils; and without backslash (\Utils::).
You can't. That's the whole point of using namespaces. You can reference a class whitout backslash or use statement only if it is in the same namespace you are using it. You can't hack the autoloader to automagically import the target class into the current namespace changing its namespace on the fly.
If your class doesn't belong to a named namespace, then it is in the global namespace and you still have to use with \ or use. The same for import or use keywords in python, java, go, .net, c/c++, etc.
There is no standard anything to do this besides using use ... as ... or "backslash" (\), however we can "cheat the PHP" using eval() within the spl_autoload_register() to extend the Utils class within the namespace.
Only use this if really necessary, prefer to use "backslash" (\) or use \Utils as Utils
Example (read comments in code):
<?php
class Utils {
public static function example() {
echo 'Hello World!';
}
}
spl_autoload_register(function($class)
{
$relative_class = strtolower(str_replace('\\', '/', $class));
$file = './src/' . $relative_class . '.php';
if (is_file($file)) {
$np = explode('\\', $class); //Dividi string
//Check if class exists in namespace (prevent conflicts)
if (class_exists(implode('::', $np)) === false) {
//Remove "class name", use only "namespace"
array_pop($np);
//evaluate a namespace in eval (extends the Utils class)
eval(
'namespace ' . implode('\\', $np) . ' {' . PHP_EOL .
'class Utils extends \Utils {}' . PHP_EOL .
'}'
);
}
require_once $file;
}
});
$user = new \Controllers\Foo\User;
$user->foo(); //Show "Hello word!"
I admit that it is an ugly hack and maybe I will not use, but it's still a hint yes.
Note: use \Utils as Utils not work in eval
I have an index.php that require Test1 class trough spl_autoload_register(). In the Test1 class the Test2 class is required with the same autoload but this error occurred:
Fatal error: DOMDocument::registerNodeClass(): Class Test2 does not
exist in...
I tried to see if the autoload work writing $test2 = new Test2(); and it works well. So with other tests I realized that with registerNodeClass() the autoload doesn't include the Test2 class file.
Is there anyone who can help me?
Test1.php
<?php
namespace Test;
use Test\Test2;
class Test1
{
function __construct($html)
{
$this->dom = new \DOMDocument();
#$this->dom->loadHTML($html);
$this->dom->registerNodeClass('DOMElement', 'Test2');
}
}
?>
Test2.php
<?php
namespace Test;
class Test2 extends \DOMElement
{
//bla, bla, bla...
}
?>
index.php
<?php
require_once('./autoload.php');
use Test\Test1;
$html = 'something';
$test = new Test1($html);
?>
autoload.php (it is the same used by Facebook for the php-sdk)
<?php
/**
* An example of a project-specific implementation.
*
* After registering this autoload function with SPL, the following line
* would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
* from /path/to/project/src/Baz/Qux.php:
*
* new \Foo\Bar\Baz\Qux;
*
* #param string $class The fully-qualified class name.
* #return void
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Test\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/src/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
?>
class Test2 is within namespace Test, so in order to do new Test2() you must be within the namespace Test or you can specify the fully qualified name (ie new Test\Test2()) to instantiate the class.
When you call $this->dom->registerNodeClass('DOMElement', 'Test2');, DOMDocument does something to the affect of:
$extendedClass = 'Test2';
$obj = new $extendedClass();
And it doesn't find Test2 because that code isn't called from the Test namespace.
So you would need to pass the fully qualified class name (w/ namespace).
Use: $this->dom->registerNodeClass('DOMElement', 'Test\Test2');
so i've started using namespaces and read some docs but I seem to be doing something wrong.
First off is my application structure which is build like this:
root
-dashboard(this is where i want to use the autoloader)
-index.php
--config(includes the autoloader)
--WePack(package)
---src(includes all my classes)
now in the src directory I included the classes with:
namespace WePack\src;
class Someclass(){
}
the content of config.php is:
<?php
// Start de sessie
ob_start();
session_start();
// Locate application path
define('ROOT', dirname(dirname(__FILE__)));
set_include_path(ROOT);
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();
echo get_include_path();
and I use it like this in my index.php
require_once ('config/config.php');
use WePack\src;
$someclass = new Someclass;
this is what the echo get_include_path(); returns:
/home/wepack/public_html/dashboard
which is what I want I guess. but the classes are not loaded and nothing is happening. I'm obviously missing something but I can't seem to figure it out. could you guys take a look at it and explain to me why this isn't working?
The problem here is, that you don't register a callback function with spl_autoload_register(). have a look at the official docs.
To be more flexible, you can write your own class to register and autoload classes like this:
class Autoloader
{
private $baseDir = null;
private function __construct($baseDir = null)
{
if ($baseDir === null) {
$this->baseDir = dirname(__FILE__);
} else {
$this->baseDir = rtrim($baseDir, '');
}
}
public static function register($baseDir = null)
{
//create an instance of the autoloader
$loader = new self($baseDir);
//register your own autoloader, which is contained in this class
spl_autoload_register(array($loader, 'autoload'));
return $loader;
}
private function autoload($class)
{
if ($class[0] === '\\') {
$class = substr($class, 1);
}
//if you want you can check if the autoloader is responsible for a specific namespace
if (strpos($class, 'yourNameSpace') !== 0) {
return;
}
//replace backslashes from the namespace with a normal directory separator
$file = sprintf('%s/%s.php', $this->baseDir, str_replace('\\', DIRECTORY_SEPARATOR, $class));
//include your file
if (is_file($file)) {
require_once($file);
}
}
}
after this you'll register your autoloader like this:
Autoloader::register("/your/path/to/your/libraries");
Isn't this what you mean:
spl_autoload_register(function( $class ) {
include_once ROOT.'/classes/'.$class.'.php';
});
That way you can just call a class like:
$user = new User(); // And loads it from "ROOT"/classes/User.php
I wrote an Autoload:
<?php
function autoload($class)
{
try
{
global $path;
$elements = explode("\\", $class);
$name_class = end($elements);
foreach ($elements as $element)
{
if($element != $name_class)
{
$path .= $element."/";
echo "path:".$path."<br/>";
}
}
$file = strtolower(($path).$name_class.".php");
if(file_exists($file))
{
include($file);
return;
}
throw new Exception($class." not founded");
}
catch(Exception $e)
{
echo $e->getMessage();
return;
}
}
class Autoloader
{
public static function autoload($class)
{
autoload($class);
}
}
spl_autoload_register('autoload');
spl_autoload_register(array('autoloader', 'autoload'));
?>
The Autoload finds a relative path and change it in an absolute path.
Now I have problem when I call a class from another file with different namespace.
For example if I have this file Base.php in the directorynamed framework:
namespace Framework;
use Framework\Inspector as Inspector;
class Base
{
private $_inspector;
public function __construct($options = array())
{
$this->_inspector = new Inspector($this);
...
Now I have the file Inspector.php in the same directory with the same namespace:
<?php
namespace Framework;
use Framework\ArrayMethods as ArrayMethods;
use Framework\StringMethods as StringMethods;
use \Exception as Exception;
class Inspector
{
...
?>
When I try to instantiate the class Base, I get the exception message about missing Inspector class.
The same think if I try to extend Base class in other directories. I tried to add some echoes in my autoload function and it printed that he use local namespace of the child class or a class who uses an instance by composition of another class to create the string for finding the php file.
For example when I try the load Inspector in Base with echo I got:
path:Framework/
file:::C:\LightTPD\htdocs\mvc\framework\framework\Base.php (I add some ::: to make more readable)
framework/base.php
path:Framework/Framework/
file:::
framework/framework/inspector.php (trying to use the namespace of inspector after using the one of Base)
Framework\Inspector not foundedpath:Framework/Framework/Framework/
file:::
framework/framework/framework/inspector.php
Framework\Inspector not founded
When I tried to extend class Base by class Driver I got this:
path:Framework/
path:Framework/Configuration/
file:::C:\LightTPD\htdocs\mvc\framework\framework\configuration\Driver.php
framework/configuration/driver.php (everything ok)
path:Framework/Configuration/Framework/ (Framework is from Base; Configuration/Framework is from driver)
file:::
framework/configuration/framework/base.php
Framework\Base not foundedpath:Framework/Configuration/Framework/Framework/
file:::
framework/configuration/framework/framework/base.php
Framework\Base not founded
-###############################################################
The directory tree is:
It's:
-framework: ArrayMethods.php, Base.php,Configuration.php, Inspector.php, StingMethods.php
-configuration: Driver.php
-exception
-core
-exception
How can I fix it?
Sorry I noticed that the problem was the global $path.
Now I wrote the autoload.php in this way:
<?php
function autoload($class)
{
try
{ $path = NULL;
$elements = explode("\\", $class);
$name_class = end($elements);
foreach ($elements as $element)
{
if($element != $name_class)
{
$path .= $element."/";
$file = realpath(strtolower(($path).$name_class.".php")); //realpath is optional
if(file_exists($file))
{
include($file);
//echo $file."<br><br>"; testing in for debugging
return;
}
}
}
throw new Exception($class." not founded");
}
catch(Exception $e)
{
echo $e->getMessage();
return;
}
}
class Autoloader
{
public static function autoload($class)
{
autoload($class);
}
}
spl_autoload_register('autoload');
spl_autoload_register(array('autoloader', 'autoload'));
?>
Now even if it's less perfomant everything it's ok, he work whatever a class call another, it's based only the namespace of a class.
Make sure you include the files path of the class you want to extend(parent class) from to your child class file before the use namespaces of the parent/Base class.