I have been fiddling around with Namespace in PHP and was trying to make it work, but it fails
Let me show the example code:
test\views\classes\MainController.php
<?php
namespace test\views\classes;
class MainController
{
public function echoData()
{
echo 'ECHOD';
}
}
test\views\index.php
<?php
require_once '..\autoloader\autoloader.php';
use test\views\classes\MainController;
$cont = new MainController();
$cont->echoData();
test\autoloader\autoloader.php
<?php
spl_autoload_register(null, FALSE);
spl_autoload_extensions('.php');
function classLoader($class)
{
$fileName = strtolower($class) . '.php';
$file = 'classes/' . $fileName;
if(!file_exists($file))
{
return FALSE;
}
include $file;
}
spl_autoload_register('classLoader');
Throws an error:
Fatal error: Class 'test\views\classes\MainController' not found in ..\test\views\index.php on line 6
Am Im missing something!
EDIT: The code works fine when both the index.php and maincontroller.php are in the same directory without using autoloader but using require_once('maincontroller.php');
Does not work if they are in different directories and with autoloader function. Can anyone sort this out.
Thanks
Multiple problems in your code:
The namespace separator (\) is not a valid path separator in Linux/Unix. Your autoloader should do something like this:
$classPath = str_replace('\\', '/', strtolower($class)) . '.php';
if (!#include_once($classPath)) {
throw new Exception('Unable to find class ' .$class);
}
Plus, the paths are all relative. You should set your include path. If your site structure is like this:
bootstrap.php
lib/
test/
views/
index.php
classes/
maincontroller.php
autoloader/
autoloader.php
Your bootstrap.php should look similar to:
$root = dirname(__FILE__);
$paths = array(
".",
$root."/lib",
get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $paths));
include 'lib/test/autoloader/autoloader.php';
Now, in your test/views/index.php you can just include the bootstrap:
include '../../bootstrap.php';
Add a die statement to your class loader:
$file = 'classes/' . $fileName;
die('File ' . $file . "\n");
And you get
File classes/test\views\classes\maincontroller.php
Is that really where your main controller class lives?
Related
Using spl_autoload_register() with a class that uses namespace, causes this error message: Fatal error: Uncaught Error: Class "MyClass" not found...
Before this error message, the autoloader does echo "...MyClass.php IS found".
Two files are used: "/index.php" (in the root directory) and "/classes/MyClass.php".
Index.php:
declare(strict_types=1);
define ('ROOT_DIR', __DIR__ . '/');
define ('CLASS_DIR', ROOT_DIR . 'classes/');
spl_autoload_register(function($class) {
$filepath = CLASS_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
if (file_exists($filepath)) {
require_once $filepath;
echo('<br /><b>' . $filepath . '</b> IS found.<br />');
} else {
echo('<br /><b>' . $filepath . '</b> not found.<br />');
exit;
}
});
$myclass = new MyClass();
MyClass.php:
declare(strict_types=1);
namespace App\Classes
class MyClass
{
// ...
}
Does anyone know how to solve this?
According to https://www.php-fig.org/psr/psr-4/, a vendor name is required, so I am using "App" as a top-level namespace name, even though it is not a directory (because my website uses the root of the server).
I also tried adding "use App/classes/Myclass" to index.php before "$myclass = new MyClass()" but this causes even more problems, because the autoloader will look for the directory "/classes/App/Classes"...
With the namespace removed from the class, everything works fine, and I can use the functions of the class through $myclass. But I would like to start using namespaces... Any help would be really appreciated! <3
Conclusion: Either the line "$myclass = new App\Classes\MyClass();"
or "use App\Classes\MyClass;" should be used.
So it is not possible to use the root of the server while also having a
top-level namespace name ("App") with this autoload function. The
function has to be expanded to allow for this possibility. And the "classes" directory will be renamed to "Classes". I will post my solution when it is ready!
For more details, read the comments below the answer by #IMSoP (Thank
you very much for your help!)
Solution:
declare(strict_types=1);
namespace App;
define ('ROOT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/');
define ('BASE_DIR', __DIR__ . '/');
define ('TOP_LEVEL_NAMESPACE_NAME', __NAMESPACE__ . '/');
spl_autoload_register(function($class) {
if (BASE_DIR == ROOT_DIR . TOP_LEVEL_NAMESPACE_NAME) {
$filepath = ROOT_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
} else {
$filepath = BASE_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
$filepath = str_replace(TOP_LEVEL_NAMESPACE_NAME, '', $filepath);
}
if (file_exists($filepath)) {
require_once $filepath;
} else {
echo('Class <b>' . end(explode('\\', $class)) . '.php</b> was not found.');
exit;
}
});
use App\Classes\MyClass;
$myclass = new MyClass();
This solution works whether the application is in the directory with the same name as the top-level namespace name, or anywhere else!
You might want to read through the manual pages on autoloading and namespaces to make sure you understand the key concepts.
You have declared a class called MyClass inside the namespace App\Classes; that means that its fully qualified name is App\Classes\MyClass - that's the name you need to call it by from outside that namespace. There could simultaneously be a different class whose fully-qualified name was just MyClass, because it wasn't in any namespace, and any number of others in other namespaces, like App\Security\MyClass, App\UI\MyClass, etc.
Then you've attempted to reference a class in index.php called MyClass, which triggers the autoloader. The autoloader translates it to a path like .../classes/MyClass.php, and loads the right file; but that file defines your namespaced class. So after the autoloader has finished, there is no class called MyClass, only App\Classes\MyClass and the code fails.
If instead you write new App\Classes\MyClass, you'll get the opposite problem: the string passed to your autoloader is 'App\Classes\MyClass' and you translate that to a file path like '.../classes/App/Classes/MyClass.php' - but that's not where your file is. (Adding use App\ClassesMyClass does the same thing - use statements are just compiler assistance to avoid writing out the fully-qualified name as often.)
What you need to do is both:
Consistently use fully-qualified class names (or alias them with use)
Lay out your files to match your namespace structure, so that your autoloader can find them, which generally means a directory per namespace
I'm working on a project whereby I have the following file structure:
index.php
|---lib
|--|lib|type|class_name.php
|--|lib|size|example_class.php
I'd like to auto load the classes, class_name and example_class (named the same as the PHP classes), so that in index.php the classes would already be instantiated so I could do:
$class_name->getPrivateParam('name');
I've had a look on the net but can't quite find the right answer - can anyone help me out?
EDIT
Thanks for the replies. Let me expand on my scenario. I'm trying to write a WordPress plugin that can be dropped into a project and additional functionality added by dropping a class into a folder 'functionality' for example, inside the plugin. There will never be 1000 classes, at a push maybe 10?
I could write a method to iterate through the folder structure of the 'lib' folder, including every class then assigning it to a variable (of the class name), but didn't think that was a very efficient way to do it but it perhaps seems that's the best way to achieve what I need?
Please, if you need to autoload classes - use the namespaces and class names conventions with SPL autoload, it will save your time for refactoring.
And of course, you will need to instantiate every class as an object.
Thank you.
Like in this thread:
PHP Autoloading in Namespaces
But if you want a complex workaround, please take a look at Symfony's autoload class:
https://github.com/symfony/ClassLoader/blob/master/ClassLoader.php
Or like this (I did it in one of my projects):
<?
spl_autoload_register(function($className)
{
$namespace=str_replace("\\","/",__NAMESPACE__);
$className=str_replace("\\","/",$className);
$class=CORE_PATH."/classes/".(empty($namespace)?"":$namespace."/")."{$className}.class.php";
include_once($class);
});
?>
and then you can instantiate your class like this:
<?
$example=new NS1\NS2\ExampleClass($exampleConstructParam);
?>
and this is your class (found in /NS1/NS2/ExampleClass.class.php):
<?
namespace NS1\NS2
{
class Symbols extends \DB\Table
{
public function __construct($param)
{
echo "hello!";
}
}
}
?>
If you have an access to the command line, you can try it with composer in the classMap section with something like this:
{
"autoload": {
"classmap": ["yourpath/", "anotherpath/"]
}
}
then you have a wordpress plugin to enable composer in the wordpress cli : http://wordpress.org/plugins/composer/
function __autoload($class_name) {
$class_name = strtolower($class_name);
$path = "{$class_name}.php";
if (file_exists($path)) {
require_once($path);
} else {
die("The file {$class_name}.php could not be found!");
}
}
UPDATE:
__autoload() is deprecated as of PHP 7.2
http://php.net/manual/de/function.spl-autoload-register.php
spl_autoload_register(function ($class) {
#require_once('lib/type/' . $class . '.php');
#require_once('lib/size/' . $class . '.php');
});
I have an example here that I use for autoloading and initiliazing.
Basically a better version of spl_autoload_register since it only tries to require the class file whenever you initializes the class.
Here it automatically gets every file inside your class folder, requires the files and initializes it. All you have to do, is name the class the same as the file.
index.php
<?php
require_once __DIR__ . '/app/autoload.php';
$loader = new Loader(false);
User::dump(['hello' => 'test']);
autoload.php
<?php
class Loader
{
public static $library;
protected static $classPath = __DIR__ . "/classes/";
protected static $interfacePath = __DIR__ . "/classes/interfaces/";
public function __construct($requireInterface = true)
{
if(!isset(static::$library)) {
// Get all files inside the class folder
foreach(array_map('basename', glob(static::$classPath . "*.php", GLOB_BRACE)) as $classExt) {
// Make sure the class is not already declared
if(!in_array($classExt, get_declared_classes())) {
// Get rid of php extension easily without pathinfo
$classNoExt = substr($classExt, 0, -4);
$file = static::$path . $classExt;
if($requireInterface) {
// Get interface file
$interface = static::$interfacePath . $classExt;
// Check if interface file exists
if(!file_exists($interface)) {
// Throw exception
die("Unable to load interface file: " . $interface);
}
// Require interface
require_once $interface;
//Check if interface is set
if(!interface_exists("Interface" . $classNoExt)) {
// Throw exception
die("Unable to find interface: " . $interface);
}
}
// Require class
require_once $file;
// Check if class file exists
if(class_exists($classNoExt)) {
// Set class // class.container.php
static::$library[$classNoExt] = new $classNoExt();
} else {
// Throw error
die("Unable to load class: " . $classNoExt);
}
}
}
}
}
/*public function get($class)
{
return (in_array($class, get_declared_classes()) ? static::$library[$class] : die("Class <b>{$class}</b> doesn't exist."));
}*/
}
You can easily manage with a bit of coding, to require classes in different folders too. Hopefully this can be of some use to you.
You can specify a namespaces-friendly autoloading using this autoloader.
<?php
spl_autoload_register(function($className) {
$file = __DIR__ . '\\' . $className . '.php';
$file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
if (file_exists($file)) {
include $file;
}
});
Make sure that you specify the class file's location corretly.
Source
spl_autoload_register(function ($class_name) {
$iterator = new DirectoryIterator(dirname(__FILE__));
$files = $iterator->getPath()."/classes/".$class_name.".class.php";
if (file_exists($files)) {
include($files);
} else {
die("Warning:The file {$files}.class.php could not be found!");
}
});
do this in a file and called it anything like (mr_load.php)
this were u put all your classes
spl_autoload_register(function($class){
$path = '\Applicaton/classes/';
$extension = '.php';
$fileName = $path.$class.$extension;
include $_SERVER['DOCUMENT_ROOT'].$fileName;
})
;
then create another file and include mr_load.php; $load_class = new BusStop(); $load_class->method()
I am following WordPress's naming convention where a class My_Class should reside in the file named class-my-class.php. I used this autoloader for WordPress, written by Rarst. If I print out the $class_name variable, I see that the prefix class is appended to the folder name and not the class file. I had the same issue with some other autoloader I used earlier. I can do a little bit of string manipulation and get what I want but I want to know what is the exact issue.
What could be wrong?
I just had a look at this autoloader you linked to, I would say it should be on line 21 something like this :
$class_path = $this->dir . '/class-' . strtolower( str_replace( '_', '-', basename( $class_name ) ) ) . '.php';
basename takes only the file part + file extension of the path.
You need also to check where this autoloader file is, because $this->dir is set to DIR, which is the directory where the autoloader file is.
Use a flexible loader.
try this one.
function TR_Autoloader($className)
{
$assetList = array(
get_stylesheet_directory() . '/vendor/log4php/Logger.php',
// added to fix woocommerce wp_email class not found issue
WP_PLUGIN_DIR . '/woocommerce/includes/libraries/class-emogrifier.php'
// add more paths if needed.
);
// normalized classes first.
$path = get_stylesheet_directory() . '/classes/class-';
$fullPath = $path . $className . '.php';
if (file_exists($fullPath)) {
include_once $fullPath;
}
if (class_exists($className)) {
return;
} else { // read the rest of the asset locations.
foreach ($assetList as $currentAsset) {
if (is_dir($currentAsset)) {
foreach (new DirectoryIterator($currentAsset) as $currentFile) {
if (!($currentFile->isDot() || ($currentFile->getExtension() <> "php")))
require_once $currentAsset . $currentFile->getFilename();
}
} elseif (is_file($currentAsset)) {
require_once $currentAsset;
}
}
}
}
spl_autoload_register('TR_Autoloader');
Can you help me connect my php files from different folders..
I have a main folder sample then inside it I have 2 folders naming 0lib and Portal.
Inside the folder 0lib there is folder amazon and inside it there are folders Mock, Model, Samples and some php files like Client.php , Model.php
Inside the folder Portal I have the productFeed.php
I already connect those files using include() and require(). I also use autoload Class... They seems ok but when I run it the error says...
Fatal error: Class 'amazon_Client' not found in /var/www/html/sample/0lib/amazon/Samples/SubmitFeedSample.php on line 68
The SubmitFeedSample.php is inside the folder 0lib->amazon->Samples->SubmitFeedSample.php
Here is my autoload Class codes:
function __autoload($className){
$filePath = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
$includePaths = explode(PATH_SEPARATOR, get_include_path());
foreach($includePaths as $includePath){
if(file_exists($includePath . DIRECTORY_SEPARATOR . $filePath)){
require_once $filePath;
return;
}
}
}
I think the auto load is the problem here.
I got your function to work as is by making one change:
function __autoload($className){
$filePath = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
$includePaths = explode(PATH_SEPARATOR, get_include_path());
foreach($includePaths as $includePath){
// Assign the compiled file path here
if(file_exists($classdir = $includePath . DIRECTORY_SEPARATOR . $filePath)){
// Don't use $filePath but rather newly assigned $classdir
require_once($classdir);
return;
}
}
}
EDIT:
The function works, but I think you are missing the relative (or absolute) path. Try creating a config.php file that you can include on all your pages that will set the root path:
root/config.php
define("ROOTDIR",__DIR__);
spl_autoload_register('__autoload');
root/some/document.php
include_once(__DIR__.'/../config.php');
$test = new test_Test();
function.__autoload()
function __autoload($className)
{
$filePath = str_replace('_',"/",$className).'.php';
// This would yield something like:
// /data/19/2/133/150/2948313/user/3268049/htdocs/mydomain/test/Test.php
$classdir = ROOTDIR."/".$filePath;
if(file_exists($classdir)) {
require_once($classdir);
return;
}
}
test_Test Class:
This class would need to be here: ROOTDIR.'/test/Test.php'
I'm working on a project whereby I have the following file structure:
index.php
|---lib
|--|lib|type|class_name.php
|--|lib|size|example_class.php
I'd like to auto load the classes, class_name and example_class (named the same as the PHP classes), so that in index.php the classes would already be instantiated so I could do:
$class_name->getPrivateParam('name');
I've had a look on the net but can't quite find the right answer - can anyone help me out?
EDIT
Thanks for the replies. Let me expand on my scenario. I'm trying to write a WordPress plugin that can be dropped into a project and additional functionality added by dropping a class into a folder 'functionality' for example, inside the plugin. There will never be 1000 classes, at a push maybe 10?
I could write a method to iterate through the folder structure of the 'lib' folder, including every class then assigning it to a variable (of the class name), but didn't think that was a very efficient way to do it but it perhaps seems that's the best way to achieve what I need?
Please, if you need to autoload classes - use the namespaces and class names conventions with SPL autoload, it will save your time for refactoring.
And of course, you will need to instantiate every class as an object.
Thank you.
Like in this thread:
PHP Autoloading in Namespaces
But if you want a complex workaround, please take a look at Symfony's autoload class:
https://github.com/symfony/ClassLoader/blob/master/ClassLoader.php
Or like this (I did it in one of my projects):
<?
spl_autoload_register(function($className)
{
$namespace=str_replace("\\","/",__NAMESPACE__);
$className=str_replace("\\","/",$className);
$class=CORE_PATH."/classes/".(empty($namespace)?"":$namespace."/")."{$className}.class.php";
include_once($class);
});
?>
and then you can instantiate your class like this:
<?
$example=new NS1\NS2\ExampleClass($exampleConstructParam);
?>
and this is your class (found in /NS1/NS2/ExampleClass.class.php):
<?
namespace NS1\NS2
{
class Symbols extends \DB\Table
{
public function __construct($param)
{
echo "hello!";
}
}
}
?>
If you have an access to the command line, you can try it with composer in the classMap section with something like this:
{
"autoload": {
"classmap": ["yourpath/", "anotherpath/"]
}
}
then you have a wordpress plugin to enable composer in the wordpress cli : http://wordpress.org/plugins/composer/
function __autoload($class_name) {
$class_name = strtolower($class_name);
$path = "{$class_name}.php";
if (file_exists($path)) {
require_once($path);
} else {
die("The file {$class_name}.php could not be found!");
}
}
UPDATE:
__autoload() is deprecated as of PHP 7.2
http://php.net/manual/de/function.spl-autoload-register.php
spl_autoload_register(function ($class) {
#require_once('lib/type/' . $class . '.php');
#require_once('lib/size/' . $class . '.php');
});
I have an example here that I use for autoloading and initiliazing.
Basically a better version of spl_autoload_register since it only tries to require the class file whenever you initializes the class.
Here it automatically gets every file inside your class folder, requires the files and initializes it. All you have to do, is name the class the same as the file.
index.php
<?php
require_once __DIR__ . '/app/autoload.php';
$loader = new Loader(false);
User::dump(['hello' => 'test']);
autoload.php
<?php
class Loader
{
public static $library;
protected static $classPath = __DIR__ . "/classes/";
protected static $interfacePath = __DIR__ . "/classes/interfaces/";
public function __construct($requireInterface = true)
{
if(!isset(static::$library)) {
// Get all files inside the class folder
foreach(array_map('basename', glob(static::$classPath . "*.php", GLOB_BRACE)) as $classExt) {
// Make sure the class is not already declared
if(!in_array($classExt, get_declared_classes())) {
// Get rid of php extension easily without pathinfo
$classNoExt = substr($classExt, 0, -4);
$file = static::$path . $classExt;
if($requireInterface) {
// Get interface file
$interface = static::$interfacePath . $classExt;
// Check if interface file exists
if(!file_exists($interface)) {
// Throw exception
die("Unable to load interface file: " . $interface);
}
// Require interface
require_once $interface;
//Check if interface is set
if(!interface_exists("Interface" . $classNoExt)) {
// Throw exception
die("Unable to find interface: " . $interface);
}
}
// Require class
require_once $file;
// Check if class file exists
if(class_exists($classNoExt)) {
// Set class // class.container.php
static::$library[$classNoExt] = new $classNoExt();
} else {
// Throw error
die("Unable to load class: " . $classNoExt);
}
}
}
}
}
/*public function get($class)
{
return (in_array($class, get_declared_classes()) ? static::$library[$class] : die("Class <b>{$class}</b> doesn't exist."));
}*/
}
You can easily manage with a bit of coding, to require classes in different folders too. Hopefully this can be of some use to you.
You can specify a namespaces-friendly autoloading using this autoloader.
<?php
spl_autoload_register(function($className) {
$file = __DIR__ . '\\' . $className . '.php';
$file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
if (file_exists($file)) {
include $file;
}
});
Make sure that you specify the class file's location corretly.
Source
spl_autoload_register(function ($class_name) {
$iterator = new DirectoryIterator(dirname(__FILE__));
$files = $iterator->getPath()."/classes/".$class_name.".class.php";
if (file_exists($files)) {
include($files);
} else {
die("Warning:The file {$files}.class.php could not be found!");
}
});
do this in a file and called it anything like (mr_load.php)
this were u put all your classes
spl_autoload_register(function($class){
$path = '\Applicaton/classes/';
$extension = '.php';
$fileName = $path.$class.$extension;
include $_SERVER['DOCUMENT_ROOT'].$fileName;
})
;
then create another file and include mr_load.php; $load_class = new BusStop(); $load_class->method()