PHP Autoloader - For classes, traits and interfaces
up vote
2
down vote
favorite
Please review my Autoloader, which is the first file included in index.php
Note that there is no resource with the same name for trait, interface and class. Each resource has it's own name. And even if some class is named "Ha" and some trait is named "Ha", one of them has specific namespace for this.
<?php
### Autoload class ###
class Autoload
{
# The path for classes, traits or interfaces #
private static $path = 'private/Model/';
# The resource files by default will be NULL #
private static $resource_files = null;
# Search for resource_files in recursive mode #
private static function recursive_glob($pattern)
{
$resource_files = glob($pattern);
foreach(glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $directory)
{
$resource_files = array_merge(
$resource_files,
self::recursive_glob($directory.'/'.basename($pattern))
);
}
return $resource_files;
}
# Order array to start with files that has the name like the $resource or closely... #
private static function order_resource_files($resource)
{
$resource_name = explode('\', $resource);
$resource_name = end($resource_name);
foreach(self::$resource_files as $file_key => $file)
{
if(stripos($file, $resource_name) !== false)
{
self::$resource_files[-1] = $file;
unset(self::$resource_files[$file_key]);
ksort(self::$resource_files);
self::$resource_files = array_values(self::$resource_files);
}
}
}
# Find resource #
public static function find($resource)
{
# Set $resource_files if is NULL #
if(!self::$resource_files)
{
self::$resource_files = self::recursive_glob(self::$path.'*.php');
}
# Order $resource_files by given resource #
self::order_resource_files($resource);
# Now that we have the $resource_files array, we can start searching #
foreach(self::$resource_files as $file_key => $file)
{
# First, remove the file from array #
unset(self::$resource_files[$file_key]);
# Include file #
require_once $file;
# If resource exists now, then we can stop and search for other resources later #
if(class_exists($resource) || trait_exists($resource) || interface_exists($resource))
{
break;
}
}
}
}
### Check PHP version and call Autoload::find ###
if(version_compare(PHP_VERSION, '5.1.2', '>='))
{
if(version_compare(PHP_VERSION, '5.3.0', '>='))
{
spl_autoload_register('Autoload::find', true, true);
}
else
{
spl_autoload_register('Autoload::find');
}
}
else
{
function __autoload($resource)
{
Autoload::find($resource);
}
}
php object-oriented library dynamic-loading
bumped to the homepage by Community♦ 27 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
up vote
2
down vote
favorite
Please review my Autoloader, which is the first file included in index.php
Note that there is no resource with the same name for trait, interface and class. Each resource has it's own name. And even if some class is named "Ha" and some trait is named "Ha", one of them has specific namespace for this.
<?php
### Autoload class ###
class Autoload
{
# The path for classes, traits or interfaces #
private static $path = 'private/Model/';
# The resource files by default will be NULL #
private static $resource_files = null;
# Search for resource_files in recursive mode #
private static function recursive_glob($pattern)
{
$resource_files = glob($pattern);
foreach(glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $directory)
{
$resource_files = array_merge(
$resource_files,
self::recursive_glob($directory.'/'.basename($pattern))
);
}
return $resource_files;
}
# Order array to start with files that has the name like the $resource or closely... #
private static function order_resource_files($resource)
{
$resource_name = explode('\', $resource);
$resource_name = end($resource_name);
foreach(self::$resource_files as $file_key => $file)
{
if(stripos($file, $resource_name) !== false)
{
self::$resource_files[-1] = $file;
unset(self::$resource_files[$file_key]);
ksort(self::$resource_files);
self::$resource_files = array_values(self::$resource_files);
}
}
}
# Find resource #
public static function find($resource)
{
# Set $resource_files if is NULL #
if(!self::$resource_files)
{
self::$resource_files = self::recursive_glob(self::$path.'*.php');
}
# Order $resource_files by given resource #
self::order_resource_files($resource);
# Now that we have the $resource_files array, we can start searching #
foreach(self::$resource_files as $file_key => $file)
{
# First, remove the file from array #
unset(self::$resource_files[$file_key]);
# Include file #
require_once $file;
# If resource exists now, then we can stop and search for other resources later #
if(class_exists($resource) || trait_exists($resource) || interface_exists($resource))
{
break;
}
}
}
}
### Check PHP version and call Autoload::find ###
if(version_compare(PHP_VERSION, '5.1.2', '>='))
{
if(version_compare(PHP_VERSION, '5.3.0', '>='))
{
spl_autoload_register('Autoload::find', true, true);
}
else
{
spl_autoload_register('Autoload::find');
}
}
else
{
function __autoload($resource)
{
Autoload::find($resource);
}
}
php object-oriented library dynamic-loading
bumped to the homepage by Community♦ 27 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Please review my Autoloader, which is the first file included in index.php
Note that there is no resource with the same name for trait, interface and class. Each resource has it's own name. And even if some class is named "Ha" and some trait is named "Ha", one of them has specific namespace for this.
<?php
### Autoload class ###
class Autoload
{
# The path for classes, traits or interfaces #
private static $path = 'private/Model/';
# The resource files by default will be NULL #
private static $resource_files = null;
# Search for resource_files in recursive mode #
private static function recursive_glob($pattern)
{
$resource_files = glob($pattern);
foreach(glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $directory)
{
$resource_files = array_merge(
$resource_files,
self::recursive_glob($directory.'/'.basename($pattern))
);
}
return $resource_files;
}
# Order array to start with files that has the name like the $resource or closely... #
private static function order_resource_files($resource)
{
$resource_name = explode('\', $resource);
$resource_name = end($resource_name);
foreach(self::$resource_files as $file_key => $file)
{
if(stripos($file, $resource_name) !== false)
{
self::$resource_files[-1] = $file;
unset(self::$resource_files[$file_key]);
ksort(self::$resource_files);
self::$resource_files = array_values(self::$resource_files);
}
}
}
# Find resource #
public static function find($resource)
{
# Set $resource_files if is NULL #
if(!self::$resource_files)
{
self::$resource_files = self::recursive_glob(self::$path.'*.php');
}
# Order $resource_files by given resource #
self::order_resource_files($resource);
# Now that we have the $resource_files array, we can start searching #
foreach(self::$resource_files as $file_key => $file)
{
# First, remove the file from array #
unset(self::$resource_files[$file_key]);
# Include file #
require_once $file;
# If resource exists now, then we can stop and search for other resources later #
if(class_exists($resource) || trait_exists($resource) || interface_exists($resource))
{
break;
}
}
}
}
### Check PHP version and call Autoload::find ###
if(version_compare(PHP_VERSION, '5.1.2', '>='))
{
if(version_compare(PHP_VERSION, '5.3.0', '>='))
{
spl_autoload_register('Autoload::find', true, true);
}
else
{
spl_autoload_register('Autoload::find');
}
}
else
{
function __autoload($resource)
{
Autoload::find($resource);
}
}
php object-oriented library dynamic-loading
Please review my Autoloader, which is the first file included in index.php
Note that there is no resource with the same name for trait, interface and class. Each resource has it's own name. And even if some class is named "Ha" and some trait is named "Ha", one of them has specific namespace for this.
<?php
### Autoload class ###
class Autoload
{
# The path for classes, traits or interfaces #
private static $path = 'private/Model/';
# The resource files by default will be NULL #
private static $resource_files = null;
# Search for resource_files in recursive mode #
private static function recursive_glob($pattern)
{
$resource_files = glob($pattern);
foreach(glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $directory)
{
$resource_files = array_merge(
$resource_files,
self::recursive_glob($directory.'/'.basename($pattern))
);
}
return $resource_files;
}
# Order array to start with files that has the name like the $resource or closely... #
private static function order_resource_files($resource)
{
$resource_name = explode('\', $resource);
$resource_name = end($resource_name);
foreach(self::$resource_files as $file_key => $file)
{
if(stripos($file, $resource_name) !== false)
{
self::$resource_files[-1] = $file;
unset(self::$resource_files[$file_key]);
ksort(self::$resource_files);
self::$resource_files = array_values(self::$resource_files);
}
}
}
# Find resource #
public static function find($resource)
{
# Set $resource_files if is NULL #
if(!self::$resource_files)
{
self::$resource_files = self::recursive_glob(self::$path.'*.php');
}
# Order $resource_files by given resource #
self::order_resource_files($resource);
# Now that we have the $resource_files array, we can start searching #
foreach(self::$resource_files as $file_key => $file)
{
# First, remove the file from array #
unset(self::$resource_files[$file_key]);
# Include file #
require_once $file;
# If resource exists now, then we can stop and search for other resources later #
if(class_exists($resource) || trait_exists($resource) || interface_exists($resource))
{
break;
}
}
}
}
### Check PHP version and call Autoload::find ###
if(version_compare(PHP_VERSION, '5.1.2', '>='))
{
if(version_compare(PHP_VERSION, '5.3.0', '>='))
{
spl_autoload_register('Autoload::find', true, true);
}
else
{
spl_autoload_register('Autoload::find');
}
}
else
{
function __autoload($resource)
{
Autoload::find($resource);
}
}
php object-oriented library dynamic-loading
php object-oriented library dynamic-loading
edited May 19 at 21:18
200_success
127k15148412
127k15148412
asked Feb 17 at 19:05
Andrei
111
111
bumped to the homepage by Community♦ 27 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
bumped to the homepage by Community♦ 27 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
up vote
0
down vote
First of all you should use static methods and properties only
when there is good reason to do so. Generally you should default to non-static.
$pathproperty can beconstbecauseprivate staticdoesn't say whether it is immutable or not.I think you shouldn't order resource files at all. You already have resource name. All you should do is match something like
$resource.'.'and include file if such exists. Also if you didrequire_oncei don't see the need to check forclass_exists()etc.I have never written autoloader but my instinct tells me that if you failed to find resource file then you should throw exception.
Altering array inside foreach is murky and its good to avoid such practices.
PS: Personally I haven't seen any php project/library utilizing hashtag comments. It might be my OCD but #...# draws all my focus so my suggestion is to use either: // for line comments and /*...*/ for block comments.
Update:
What I had in mind when writing point 2 is something like this:
foreach(self::$resource_files as $file_key => $file)
{
// Check if resource matches file name
if (strpos($file, $resource.'.') !== false ) {
unset(self::$resource_files[$file_key]);
require_once $file;
}
}
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
add a comment |
up vote
0
down vote
First, I disagree with the comment of xReprisal about static. A Static method is a useful tool and spoiling it is a waste. However, you are also using a static field and it is not recommended (because it's not safe, at least on Java). I avoid (my example, see below) to use static by creating a new instance once.
For this task, I create a project (free, MIT license) that it does the auto mapper
https://github.com/EFTEC/AutoLoadOne
It generates an automap from all the classes, interfaces and traits and it produces an autoload.php. It does the autoload the most efficiently possible (I hope). So it's a two stage program.
About your code:
a) PHP 5.6 will be close to being obsolete. So, I don't think it's worth to support an old version of PHP.
b) You are scanning an entire folder (and their subfolders) each time you call a new class/interface/trait ($resource_files = glob($pattern); instead of self::resource_files i.e the recommendation of xReprisal). Even if you are doing the scan of the entire folder once, you are doing it once per customer per call. So it impacts the performance. If you can, you could cache the $self:: resource data on APCU, REDIS or even in a text field). What I did to solve it? pre-scan the folder once and for all by generating the class:
The class has two arrays, one if for map classes that are specials such as classes in different folders or classes in the same .php file.
And the second array has a map of namespaces and folders.
It is how a generated autoload.php looks:
<?php
/**
* This class is used for autocomplete.
* Class _AutoLoad
* @noautoload it avoids to index this class
* @generated by AutoLoadOne 1.3 generated 2018/07/06 05:39:35
* @copyright Copyright Jorge Castro C - MIT License. https://github.com/EFTEC/AutoLoadOne
*/
class _AutoLoad
{
var $debug=false;
private $_arrautoloadCustom = array(
'MyProjectConnection' => '/../test/folder/multiplenamespace.php',
'AnotherProjectConnection' => '/../test/folder/multiplenamespace.php',
'MyProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'AnotherProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'ClassWithoutNameSpace' => '/../test/folder/subfolderalt/ClassWithoutNameSpace.php',
'foldersubfolderCustomClass' => '/../test/folder/subfolderalt/CustomClass.php'
);
private $_arrautoload = array(
'folder' => '/../test/folder',
'foldersubfolder' => '/../test/folder/subfolder',
'subsubsub' => '/../test/folder/subfolder/subsubfolder'
);
/**
* _AutoLoad constructor.
* @param bool $debug
*/
public function __construct($debug=false)
{
$this->debug = $debug;
}
/**
* @param $class_name
* @throws Exception
*/
public function auto($class_name) {
// its called only if the class is not loaded.
$ns = dirname($class_name); // without trailing
$ns=($ns==".")?"":$ns;
$cls = basename($class_name);
// special cases
if (isset($this->_arrautoloadCustom[$class_name])) {
$this->loadIfExists($this->_arrautoloadCustom[$class_name] );
return;
}
// normal (folder) cases
if (isset($this->_arrautoload[$ns])) {
$this->loadIfExists($this->_arrautoload[$ns] . "/" . $cls . ".php");
return;
}
}
/**
* @param $filename
* @throws Exception
*/
public function loadIfExists($filename)
{
if((@include __DIR__."/".$filename) === false) {
if ($this->debug) {
throw new Exception("AutoLoadOne Error: Loading file [".__DIR__."/".$filename."] for class [".basename($filename)."]");
} else {
throw new Exception("AutoLoadOne Error: No file found.");
}
}
}
} // end of the class _AutoLoad
if (defined('_AUTOLOADONEDEBUG')) {
$_autoLoad=new _AutoLoad(_AUTOLOADONEDEBUG);
} else {
$_autoLoad=new _AutoLoad(false);
}
spl_autoload_register(function ($class_name)
{
global $_autoLoad;
$_autoLoad->auto($class_name);
});
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
First of all you should use static methods and properties only
when there is good reason to do so. Generally you should default to non-static.
$pathproperty can beconstbecauseprivate staticdoesn't say whether it is immutable or not.I think you shouldn't order resource files at all. You already have resource name. All you should do is match something like
$resource.'.'and include file if such exists. Also if you didrequire_oncei don't see the need to check forclass_exists()etc.I have never written autoloader but my instinct tells me that if you failed to find resource file then you should throw exception.
Altering array inside foreach is murky and its good to avoid such practices.
PS: Personally I haven't seen any php project/library utilizing hashtag comments. It might be my OCD but #...# draws all my focus so my suggestion is to use either: // for line comments and /*...*/ for block comments.
Update:
What I had in mind when writing point 2 is something like this:
foreach(self::$resource_files as $file_key => $file)
{
// Check if resource matches file name
if (strpos($file, $resource.'.') !== false ) {
unset(self::$resource_files[$file_key]);
require_once $file;
}
}
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
add a comment |
up vote
0
down vote
First of all you should use static methods and properties only
when there is good reason to do so. Generally you should default to non-static.
$pathproperty can beconstbecauseprivate staticdoesn't say whether it is immutable or not.I think you shouldn't order resource files at all. You already have resource name. All you should do is match something like
$resource.'.'and include file if such exists. Also if you didrequire_oncei don't see the need to check forclass_exists()etc.I have never written autoloader but my instinct tells me that if you failed to find resource file then you should throw exception.
Altering array inside foreach is murky and its good to avoid such practices.
PS: Personally I haven't seen any php project/library utilizing hashtag comments. It might be my OCD but #...# draws all my focus so my suggestion is to use either: // for line comments and /*...*/ for block comments.
Update:
What I had in mind when writing point 2 is something like this:
foreach(self::$resource_files as $file_key => $file)
{
// Check if resource matches file name
if (strpos($file, $resource.'.') !== false ) {
unset(self::$resource_files[$file_key]);
require_once $file;
}
}
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
add a comment |
up vote
0
down vote
up vote
0
down vote
First of all you should use static methods and properties only
when there is good reason to do so. Generally you should default to non-static.
$pathproperty can beconstbecauseprivate staticdoesn't say whether it is immutable or not.I think you shouldn't order resource files at all. You already have resource name. All you should do is match something like
$resource.'.'and include file if such exists. Also if you didrequire_oncei don't see the need to check forclass_exists()etc.I have never written autoloader but my instinct tells me that if you failed to find resource file then you should throw exception.
Altering array inside foreach is murky and its good to avoid such practices.
PS: Personally I haven't seen any php project/library utilizing hashtag comments. It might be my OCD but #...# draws all my focus so my suggestion is to use either: // for line comments and /*...*/ for block comments.
Update:
What I had in mind when writing point 2 is something like this:
foreach(self::$resource_files as $file_key => $file)
{
// Check if resource matches file name
if (strpos($file, $resource.'.') !== false ) {
unset(self::$resource_files[$file_key]);
require_once $file;
}
}
First of all you should use static methods and properties only
when there is good reason to do so. Generally you should default to non-static.
$pathproperty can beconstbecauseprivate staticdoesn't say whether it is immutable or not.I think you shouldn't order resource files at all. You already have resource name. All you should do is match something like
$resource.'.'and include file if such exists. Also if you didrequire_oncei don't see the need to check forclass_exists()etc.I have never written autoloader but my instinct tells me that if you failed to find resource file then you should throw exception.
Altering array inside foreach is murky and its good to avoid such practices.
PS: Personally I haven't seen any php project/library utilizing hashtag comments. It might be my OCD but #...# draws all my focus so my suggestion is to use either: // for line comments and /*...*/ for block comments.
Update:
What I had in mind when writing point 2 is something like this:
foreach(self::$resource_files as $file_key => $file)
{
// Check if resource matches file name
if (strpos($file, $resource.'.') !== false ) {
unset(self::$resource_files[$file_key]);
require_once $file;
}
}
edited Feb 18 at 19:57
answered Feb 18 at 15:23
xReprisal
29114
29114
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
add a comment |
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
1. Yes. It can be const and I would do that ! 2. If you test the Autoloader you will see that there is a reason why I use class_exists etc. If I don't do that, the foreach will include all classes without any sense. This is also the reason why I reorder the array. 3. Yes, you are right ! 4. Yes, I know that altering array inside foreach sounds bad, but in my case it has a good point. If I don't delete array elements, then I will end including all files over and over. Thank you very much for your answer!
– Andrei
Feb 18 at 18:21
add a comment |
up vote
0
down vote
First, I disagree with the comment of xReprisal about static. A Static method is a useful tool and spoiling it is a waste. However, you are also using a static field and it is not recommended (because it's not safe, at least on Java). I avoid (my example, see below) to use static by creating a new instance once.
For this task, I create a project (free, MIT license) that it does the auto mapper
https://github.com/EFTEC/AutoLoadOne
It generates an automap from all the classes, interfaces and traits and it produces an autoload.php. It does the autoload the most efficiently possible (I hope). So it's a two stage program.
About your code:
a) PHP 5.6 will be close to being obsolete. So, I don't think it's worth to support an old version of PHP.
b) You are scanning an entire folder (and their subfolders) each time you call a new class/interface/trait ($resource_files = glob($pattern); instead of self::resource_files i.e the recommendation of xReprisal). Even if you are doing the scan of the entire folder once, you are doing it once per customer per call. So it impacts the performance. If you can, you could cache the $self:: resource data on APCU, REDIS or even in a text field). What I did to solve it? pre-scan the folder once and for all by generating the class:
The class has two arrays, one if for map classes that are specials such as classes in different folders or classes in the same .php file.
And the second array has a map of namespaces and folders.
It is how a generated autoload.php looks:
<?php
/**
* This class is used for autocomplete.
* Class _AutoLoad
* @noautoload it avoids to index this class
* @generated by AutoLoadOne 1.3 generated 2018/07/06 05:39:35
* @copyright Copyright Jorge Castro C - MIT License. https://github.com/EFTEC/AutoLoadOne
*/
class _AutoLoad
{
var $debug=false;
private $_arrautoloadCustom = array(
'MyProjectConnection' => '/../test/folder/multiplenamespace.php',
'AnotherProjectConnection' => '/../test/folder/multiplenamespace.php',
'MyProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'AnotherProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'ClassWithoutNameSpace' => '/../test/folder/subfolderalt/ClassWithoutNameSpace.php',
'foldersubfolderCustomClass' => '/../test/folder/subfolderalt/CustomClass.php'
);
private $_arrautoload = array(
'folder' => '/../test/folder',
'foldersubfolder' => '/../test/folder/subfolder',
'subsubsub' => '/../test/folder/subfolder/subsubfolder'
);
/**
* _AutoLoad constructor.
* @param bool $debug
*/
public function __construct($debug=false)
{
$this->debug = $debug;
}
/**
* @param $class_name
* @throws Exception
*/
public function auto($class_name) {
// its called only if the class is not loaded.
$ns = dirname($class_name); // without trailing
$ns=($ns==".")?"":$ns;
$cls = basename($class_name);
// special cases
if (isset($this->_arrautoloadCustom[$class_name])) {
$this->loadIfExists($this->_arrautoloadCustom[$class_name] );
return;
}
// normal (folder) cases
if (isset($this->_arrautoload[$ns])) {
$this->loadIfExists($this->_arrautoload[$ns] . "/" . $cls . ".php");
return;
}
}
/**
* @param $filename
* @throws Exception
*/
public function loadIfExists($filename)
{
if((@include __DIR__."/".$filename) === false) {
if ($this->debug) {
throw new Exception("AutoLoadOne Error: Loading file [".__DIR__."/".$filename."] for class [".basename($filename)."]");
} else {
throw new Exception("AutoLoadOne Error: No file found.");
}
}
}
} // end of the class _AutoLoad
if (defined('_AUTOLOADONEDEBUG')) {
$_autoLoad=new _AutoLoad(_AUTOLOADONEDEBUG);
} else {
$_autoLoad=new _AutoLoad(false);
}
spl_autoload_register(function ($class_name)
{
global $_autoLoad;
$_autoLoad->auto($class_name);
});
add a comment |
up vote
0
down vote
First, I disagree with the comment of xReprisal about static. A Static method is a useful tool and spoiling it is a waste. However, you are also using a static field and it is not recommended (because it's not safe, at least on Java). I avoid (my example, see below) to use static by creating a new instance once.
For this task, I create a project (free, MIT license) that it does the auto mapper
https://github.com/EFTEC/AutoLoadOne
It generates an automap from all the classes, interfaces and traits and it produces an autoload.php. It does the autoload the most efficiently possible (I hope). So it's a two stage program.
About your code:
a) PHP 5.6 will be close to being obsolete. So, I don't think it's worth to support an old version of PHP.
b) You are scanning an entire folder (and their subfolders) each time you call a new class/interface/trait ($resource_files = glob($pattern); instead of self::resource_files i.e the recommendation of xReprisal). Even if you are doing the scan of the entire folder once, you are doing it once per customer per call. So it impacts the performance. If you can, you could cache the $self:: resource data on APCU, REDIS or even in a text field). What I did to solve it? pre-scan the folder once and for all by generating the class:
The class has two arrays, one if for map classes that are specials such as classes in different folders or classes in the same .php file.
And the second array has a map of namespaces and folders.
It is how a generated autoload.php looks:
<?php
/**
* This class is used for autocomplete.
* Class _AutoLoad
* @noautoload it avoids to index this class
* @generated by AutoLoadOne 1.3 generated 2018/07/06 05:39:35
* @copyright Copyright Jorge Castro C - MIT License. https://github.com/EFTEC/AutoLoadOne
*/
class _AutoLoad
{
var $debug=false;
private $_arrautoloadCustom = array(
'MyProjectConnection' => '/../test/folder/multiplenamespace.php',
'AnotherProjectConnection' => '/../test/folder/multiplenamespace.php',
'MyProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'AnotherProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'ClassWithoutNameSpace' => '/../test/folder/subfolderalt/ClassWithoutNameSpace.php',
'foldersubfolderCustomClass' => '/../test/folder/subfolderalt/CustomClass.php'
);
private $_arrautoload = array(
'folder' => '/../test/folder',
'foldersubfolder' => '/../test/folder/subfolder',
'subsubsub' => '/../test/folder/subfolder/subsubfolder'
);
/**
* _AutoLoad constructor.
* @param bool $debug
*/
public function __construct($debug=false)
{
$this->debug = $debug;
}
/**
* @param $class_name
* @throws Exception
*/
public function auto($class_name) {
// its called only if the class is not loaded.
$ns = dirname($class_name); // without trailing
$ns=($ns==".")?"":$ns;
$cls = basename($class_name);
// special cases
if (isset($this->_arrautoloadCustom[$class_name])) {
$this->loadIfExists($this->_arrautoloadCustom[$class_name] );
return;
}
// normal (folder) cases
if (isset($this->_arrautoload[$ns])) {
$this->loadIfExists($this->_arrautoload[$ns] . "/" . $cls . ".php");
return;
}
}
/**
* @param $filename
* @throws Exception
*/
public function loadIfExists($filename)
{
if((@include __DIR__."/".$filename) === false) {
if ($this->debug) {
throw new Exception("AutoLoadOne Error: Loading file [".__DIR__."/".$filename."] for class [".basename($filename)."]");
} else {
throw new Exception("AutoLoadOne Error: No file found.");
}
}
}
} // end of the class _AutoLoad
if (defined('_AUTOLOADONEDEBUG')) {
$_autoLoad=new _AutoLoad(_AUTOLOADONEDEBUG);
} else {
$_autoLoad=new _AutoLoad(false);
}
spl_autoload_register(function ($class_name)
{
global $_autoLoad;
$_autoLoad->auto($class_name);
});
add a comment |
up vote
0
down vote
up vote
0
down vote
First, I disagree with the comment of xReprisal about static. A Static method is a useful tool and spoiling it is a waste. However, you are also using a static field and it is not recommended (because it's not safe, at least on Java). I avoid (my example, see below) to use static by creating a new instance once.
For this task, I create a project (free, MIT license) that it does the auto mapper
https://github.com/EFTEC/AutoLoadOne
It generates an automap from all the classes, interfaces and traits and it produces an autoload.php. It does the autoload the most efficiently possible (I hope). So it's a two stage program.
About your code:
a) PHP 5.6 will be close to being obsolete. So, I don't think it's worth to support an old version of PHP.
b) You are scanning an entire folder (and their subfolders) each time you call a new class/interface/trait ($resource_files = glob($pattern); instead of self::resource_files i.e the recommendation of xReprisal). Even if you are doing the scan of the entire folder once, you are doing it once per customer per call. So it impacts the performance. If you can, you could cache the $self:: resource data on APCU, REDIS or even in a text field). What I did to solve it? pre-scan the folder once and for all by generating the class:
The class has two arrays, one if for map classes that are specials such as classes in different folders or classes in the same .php file.
And the second array has a map of namespaces and folders.
It is how a generated autoload.php looks:
<?php
/**
* This class is used for autocomplete.
* Class _AutoLoad
* @noautoload it avoids to index this class
* @generated by AutoLoadOne 1.3 generated 2018/07/06 05:39:35
* @copyright Copyright Jorge Castro C - MIT License. https://github.com/EFTEC/AutoLoadOne
*/
class _AutoLoad
{
var $debug=false;
private $_arrautoloadCustom = array(
'MyProjectConnection' => '/../test/folder/multiplenamespace.php',
'AnotherProjectConnection' => '/../test/folder/multiplenamespace.php',
'MyProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'AnotherProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'ClassWithoutNameSpace' => '/../test/folder/subfolderalt/ClassWithoutNameSpace.php',
'foldersubfolderCustomClass' => '/../test/folder/subfolderalt/CustomClass.php'
);
private $_arrautoload = array(
'folder' => '/../test/folder',
'foldersubfolder' => '/../test/folder/subfolder',
'subsubsub' => '/../test/folder/subfolder/subsubfolder'
);
/**
* _AutoLoad constructor.
* @param bool $debug
*/
public function __construct($debug=false)
{
$this->debug = $debug;
}
/**
* @param $class_name
* @throws Exception
*/
public function auto($class_name) {
// its called only if the class is not loaded.
$ns = dirname($class_name); // without trailing
$ns=($ns==".")?"":$ns;
$cls = basename($class_name);
// special cases
if (isset($this->_arrautoloadCustom[$class_name])) {
$this->loadIfExists($this->_arrautoloadCustom[$class_name] );
return;
}
// normal (folder) cases
if (isset($this->_arrautoload[$ns])) {
$this->loadIfExists($this->_arrautoload[$ns] . "/" . $cls . ".php");
return;
}
}
/**
* @param $filename
* @throws Exception
*/
public function loadIfExists($filename)
{
if((@include __DIR__."/".$filename) === false) {
if ($this->debug) {
throw new Exception("AutoLoadOne Error: Loading file [".__DIR__."/".$filename."] for class [".basename($filename)."]");
} else {
throw new Exception("AutoLoadOne Error: No file found.");
}
}
}
} // end of the class _AutoLoad
if (defined('_AUTOLOADONEDEBUG')) {
$_autoLoad=new _AutoLoad(_AUTOLOADONEDEBUG);
} else {
$_autoLoad=new _AutoLoad(false);
}
spl_autoload_register(function ($class_name)
{
global $_autoLoad;
$_autoLoad->auto($class_name);
});
First, I disagree with the comment of xReprisal about static. A Static method is a useful tool and spoiling it is a waste. However, you are also using a static field and it is not recommended (because it's not safe, at least on Java). I avoid (my example, see below) to use static by creating a new instance once.
For this task, I create a project (free, MIT license) that it does the auto mapper
https://github.com/EFTEC/AutoLoadOne
It generates an automap from all the classes, interfaces and traits and it produces an autoload.php. It does the autoload the most efficiently possible (I hope). So it's a two stage program.
About your code:
a) PHP 5.6 will be close to being obsolete. So, I don't think it's worth to support an old version of PHP.
b) You are scanning an entire folder (and their subfolders) each time you call a new class/interface/trait ($resource_files = glob($pattern); instead of self::resource_files i.e the recommendation of xReprisal). Even if you are doing the scan of the entire folder once, you are doing it once per customer per call. So it impacts the performance. If you can, you could cache the $self:: resource data on APCU, REDIS or even in a text field). What I did to solve it? pre-scan the folder once and for all by generating the class:
The class has two arrays, one if for map classes that are specials such as classes in different folders or classes in the same .php file.
And the second array has a map of namespaces and folders.
It is how a generated autoload.php looks:
<?php
/**
* This class is used for autocomplete.
* Class _AutoLoad
* @noautoload it avoids to index this class
* @generated by AutoLoadOne 1.3 generated 2018/07/06 05:39:35
* @copyright Copyright Jorge Castro C - MIT License. https://github.com/EFTEC/AutoLoadOne
*/
class _AutoLoad
{
var $debug=false;
private $_arrautoloadCustom = array(
'MyProjectConnection' => '/../test/folder/multiplenamespace.php',
'AnotherProjectConnection' => '/../test/folder/multiplenamespace.php',
'MyProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'AnotherProjectConnection2' => '/../test/folder/multiplenamespace2.php',
'ClassWithoutNameSpace' => '/../test/folder/subfolderalt/ClassWithoutNameSpace.php',
'foldersubfolderCustomClass' => '/../test/folder/subfolderalt/CustomClass.php'
);
private $_arrautoload = array(
'folder' => '/../test/folder',
'foldersubfolder' => '/../test/folder/subfolder',
'subsubsub' => '/../test/folder/subfolder/subsubfolder'
);
/**
* _AutoLoad constructor.
* @param bool $debug
*/
public function __construct($debug=false)
{
$this->debug = $debug;
}
/**
* @param $class_name
* @throws Exception
*/
public function auto($class_name) {
// its called only if the class is not loaded.
$ns = dirname($class_name); // without trailing
$ns=($ns==".")?"":$ns;
$cls = basename($class_name);
// special cases
if (isset($this->_arrautoloadCustom[$class_name])) {
$this->loadIfExists($this->_arrautoloadCustom[$class_name] );
return;
}
// normal (folder) cases
if (isset($this->_arrautoload[$ns])) {
$this->loadIfExists($this->_arrautoload[$ns] . "/" . $cls . ".php");
return;
}
}
/**
* @param $filename
* @throws Exception
*/
public function loadIfExists($filename)
{
if((@include __DIR__."/".$filename) === false) {
if ($this->debug) {
throw new Exception("AutoLoadOne Error: Loading file [".__DIR__."/".$filename."] for class [".basename($filename)."]");
} else {
throw new Exception("AutoLoadOne Error: No file found.");
}
}
}
} // end of the class _AutoLoad
if (defined('_AUTOLOADONEDEBUG')) {
$_autoLoad=new _AutoLoad(_AUTOLOADONEDEBUG);
} else {
$_autoLoad=new _AutoLoad(false);
}
spl_autoload_register(function ($class_name)
{
global $_autoLoad;
$_autoLoad->auto($class_name);
});
edited Jul 6 at 21:14
answered Jul 6 at 21:09
magallanes
1012
1012
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187764%2fphp-autoloader-for-classes-traits-and-interfaces%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown