Namespaced autoloading not working properly - php

I'm having trouble getting autoloading to work with namespaces. Here's the directory structure I created for this:
index.php
app/
utils/
sys/
DirReader.php
helpers/
DB.php
The index.php contains the autoloader, which includes the files DirReader.php and DB.php.
Here's what the index.php looks like:
<?php
function __autoload($ns_str) //ns_str = namespace string
{
$path = str_replace('\\', DIRECTORY_SEPARATOR, $ns_str);
//echo "**$path**\n";
require_once "$path.php";
}
use \app\utils\sys as sys;
use \app\utils\helpers as helpers;
$dir = new sys\DirReader();
$db = new helpers\DB();
Here's DirReader.php:
<?php
namespace app\utils\sys;
class DirReader
{
public function __construct()
{
echo "DirReader object created!\n";
}
}
And here's DB.php:
<?php
namespace app\utils\helpers;
class DB
{
public function __construct()
{
echo "DB object created!\n";
}
}
The example works fine, but when I add a namespace declaration to index.php, it fails:
<?php
namespace myns;
function __autoload($ns_str) //ns_str = namespace string
{ /*. . .*/
PHP Fatal error: Class 'app\utils\sys\DirReader' not found in
/var/www/html/php_learn/autoloading_1/index.php on line 15 PHP Stack
trace: PHP 1. {main}()
/var/www/html/php_learn/autoloading_1/index.php:0
According to me this error shouldn't come because I've used absolute names when using namespaces in index.php. I know that saying something like use app\utils\sys as sys; would fail because then the namespace will be searched relative to myns, where nothing exists. But I don't know why my code is not working. (I also tried changing the name of the namespace in index.php to autoloading_1, the name of the containing directory, but it didn't help).

The __autoload function must be defined in global space.
Use spl_autoload_register otherwise. Use of __autoload() is discouraged.

Related

Fatal error: Cannot declare class

I can not understand why php gives me an error
"Fatal error: Cannot declare class rex\builder\RexBuilder, because the
name is already in use in /var/www/site2.dev/App/rex/RexBuilder.php on
line 12"
RexBuilder static class, and it is called only 1 time.
I did a search on the project, no longer classes with the same name.
<?php
namespace rex\builder;
require_once 'Router.php';
use rex\router\Router;
error_reporting(E_ALL);
ini_set('display_errors', 1);
class RexBuilder {
public static function collector($array) {
$router = new Router();
foreach ($array as $key => $val) {
$router->get($val->getMethod(), $val->getInterfaces(), $val->getHandler());
}
$router->init();
}
}
?>
Call the class in index.php
RexBuilder::collector(array(
new BuildModel('POST', '/api/v1/user/register', new \api\register\Registration()),
new BuildModel('POST', '/api/v1/user/login', new \api\login\Login())));
More This class is not used
The error is thrown because of the use rex\router\Router; duplicate classes.
When you are writing use namespace.. it means you can go directly to that namespace like it is your current namespace
Lets take a look at the next code:
We'll create a file and declare that it belongs to namespace classes\a
//file: a.php
<?php
namespace classes\a;
class A{
}
now lets create another file b.php (and declare it belongs to namespace classes\b but it means nothing for the example)
namespace classes\b;
require_once "a.php";
use classes\a; //Notice that I'm using this namespace, it means I can use it directly
class A{
}
Generates the error
Fatal error: Cannot declare class classes\b\A because the name is already in use in
We have to solutions possible:
First: remove the use tag and write the namespace directly
class A{
function __constructor(){
$instance = new classes\a\A();
}
}
Second, give it alias
use classes\a as out_a;
class A{
function __constructor(){
$instance = new out_a\A();
}
}
For your code, just remove the use or give it an alias.
The problem is certainly because you include the RexBuilder.php file two times instead of one.
If you call the file by this way : include('RexBuilder.php'); or this way require('RexBuilder.php'); please change it by include_once('RexBuilder.php'); or require_once('RexBuilder.php'); which only allows ONE call of the file.

Namespaces and spl_autoload_register

I have found several SO questions similar to mine, but am struggling to find an answer that helps me, plus I'd really like to know the best practice for autoloading classes that exist within namespaces.
My folder structure:
root
-- classes
--- Users
---- Users.class.php
And users.php;
<?php
namespace CompanyName\ProjectName\Users;
class UserMapper
{
// class code here
}
And my autoload function, which sits in the root folder;
/* autoload classes on instatiation */
spl_autoload_register(function($class)
{
include $_SERVER['DOCUMENT_ROOT'] . '/classes/' . $class . '.class.php';
});
And, let's say I call the user class like so;
<?php
new \CompanyName\ProjectName\User();
Warning: include(/Applications/XAMPP/xamppfiles/htdocs/test_tool/classes/CompanyName\ProjectName\User.class.php): failed to open stream: No such file or directory in...etc
To use spl_autoload_register, do I need to map my folder structure to my namespace structure? I would prefer not to do this as I like to have my classes in the same folder, with sub folders within.
Or do I add extra code to my autoload function?
I have also searched the php manual, and there is no working namespace example, which I find very strange.
Any help would be much appreciated.
Thanks in advance.
Disclaimer: I am a beginer at php, my answer may not be correct
but i'm confident examining and testing the example bellow will help clarify the use of Namespaces with spl_autoload_register for any beginner like me.
Consider this folder structure :
root/ contains index.php.
root/model/ contains A.php & AA.php
A.php :
?php
namespace company\model;
use \company\model\A;
class A
{
public function speak()
{
echo 'hello world! ';
}
}
AA.php :
?php
namespace company\model;
use \company\model\A;
require_once 'A.php';
class AA extends A
{
public function shout()
{
echo 'HELLO WOORLD!!!';
}
}
index.php :
<?php
namespace company;
use \company\model\A;
function classLoader ($className)
{
if (file_exists($className.'.php'))
{
require_once $className.'.php';
} else {
$className = str_replace('\\', '/', $className);
$className = str_replace('company/', '', $className);
if (file_exists($className.'.php'))
require_once $className.'.php';
else
throw new EXCEPTION('classLoader could not find '.$className.'.php .');
}
}
spl_autoload_register(classLoader);
$obj = new A;
//we dont need to write ($obj = new \company\model\A;)
//because of statement at line 4
$obj->speak();
echo '<br/>';
$objA = new \company\model\AA;
$objA->shout();
echo '<br/>';
class AB extends \company\model\AA
{
public function doBoth()
{
$this->speak();
$this->shout();
}
}
$objB = new AB;
$objB->doBoth();

PHP namespace autoload

Trying to understand how namespaces and autoload works on PHP
Server.php located at core/server.php
namespace core\server
{
class Main
{
public function getTopic()
{
$get_params = $_GET;
if (empty($get_params)) $get_params = ['subtopic' => 'test'];
return $get_params;
}
}
}
and Index.php
spl_autoload_register();
use core\server as subtopic;
$test = new subtopic\Main();
var_dump($test);
It cant load the class core/server/Main
Autoload doesn't work that way.
First I will explain how autoloaders works.
spl_autoload_register() is a function to register a function you have in your code to server as an autoloader, the standard function would be:
define('APP_PATH','/path/to/your/dir');
function auto_load($class)
{
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
The constant APP_PATH would be the path to your directory where your code lives.
As you noticed it the param that is passed to spl_autoload_register is the name of my function, this register the function so when a class is instanciated it runs that function.
Now an effective way to use autoloaders and namespaces would be the following:
file - /autoloader.php
define('APP_PATH','/path/to/your/dir');
define('DS', DIRECTORY_SEPARATOR);
function auto_load($class)
{
$class = str_replace('\\', DS, $class);
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
file - /index.php
include 'autoloader.php';
$tree = new Libs\Tree();
$apple_tree = new Libs\Tree\AppleTree();
file - /Libs/Tree.php
namespace Libs;
class Tree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
file - /Libs/Tree/AppleTree.php
namespace Libs\Tree;
class AppleTree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
I'm using namespaces and autoload to load my functions in a nicely way, you can use namespace to describe in what dir your class resides and uses the autoloader magic to load it without any problems.
Note: I used the constant 'DS', because in *nix it uses '/' and in Windows it uses '\', with DIRECTORY_SEPARATOR we don't have to worry where the code is going to run, because it will be "path-compatible"

__autoload with namespace class gives fatal error for file not found

I was going through the __autoload feature that PHP provides.
However this is the index.php I have:-
define('app_path', realpath('../'));
$paths = array(
app_path, get_include_path());
set_include_path(implode(PATH_SEPARATOR, $paths));
function __autoload($classname)
{
$filename = str_replace('\\', '/', $classname.'.php');
require_once $filename;
}
use \engine\controllers as Controllers;
$data = new Controllers\base(); // This one is line no. 25 (Here is error)
echo $data->mind('Hi');
And this one as my base.php:-
namespace controllers;
class base {
public function __construct() {
echo __CLASS__ . '<br/>';
echo __NAMESPACE__;
}
public function mind($myname)
{
echo $myname;
}
}
and throws this error:
My directory structure is as follows:
app -> engine -> controller -> base.php
app -> index.php
I am not sure whats wrong is happening. I am just learning how to use namespace and __autoload
I also tried spl_autoload_register but did not success. Kindly suggest.
EDIT:1
Also, if i want to replace it with spl_autoload_register how that can be implemented.
Not sure, but worth a try:
In base.php, change to namespace engine\controllers; on line 1.
And in index.php, change to use engine\controllers as Controllers; (remove leading backslash) on line 23.

PHP Namespaces autoload

I have the following directory structure:
/var/www/Project1/Project1.php
/var/www/Project1/User/UserProfile.php
Inside Project1.php:
<?php
namespace Project1;
set_include_path( __DIR__ );
spl_autoload_extensions('.php');
spl_autoload_register();
use User\UserProfile;
$u = new Avatar();
...
?>
Inside UserProfile.php:
<?php
namespace Project1\User;
class Avatar{
}
...
?>
When I execute php Project1.php I get:
PHP Fatal error: spl_autoload9(): Class User\UserProfile could not be loaded
I don't see the problem.
spl_autoload_register(); when called with no params will just register the default autoloader which fails to handle namespaces with your project layout. You'll have to register your own method to make it work. Like this:
spl_autoload_register('my_autoload');
And here comes the autoload function. This function expects the classes to be stored in a way like:
/path/to/project/Namespace/Classname.php
/path/to/project/Namespace/Subnamespace/Classname.php
You can name the classes like \Namespaces\Classname or the old style way Namespace_Classname:
function my_autoload ($classname) {
// if the class where already loaded. should not happen
if (class_exists($classname)) {
return true;
}
// Works for PEAR style class names and namespaced class names
$path = str_replace(
array('_', '\\'),
'/',
$classname
) . '.php';
if (file_exists('/path/to/project/' . $tail)) {
include_once 'path/to/project/' . $tail;
return true;
}
return false;
}
Note that the function is taken from my github package Jm_Autoloader. The package provides more functionality as multiple include paths, path prefixes and static autoloading (with a predefined assoc array class name => file name). You can use it if you like ;)

Categories