Is autoloader different for framework vs. normal library - php

According to the PCR-0 proposal, all the autoloader needs is autoload() function. Zend has a few autoloading classes
Zend_Loader()
Zend_Loader_Autoloader()
Zend_Loader_Autoloader_Resource()
Zend_Loader_Autoloader_Interface()
I'm guessing it has all these classes because it's a framework, so it needs to load its own classes, as well as library classes for any libraries that the developer may add and which don't have their own autoloader.
I have a library (normal library, not a framework). Right now it doesn't have an autoloader so I use Zend's Zend_Loader_Autoloader_Resource ->addResourceType(). If I write an autoloader for it, which autoloader will be used: Zend's or the library? and do I have to implement an autoloader as complex as Zend's or a simple one like the PCR-0 example.
What happens in these cases
both framework and library have their own autoloader
framework has autoloader, but the library doesn't
framework has NO autoloader, and library has

I'd suggest sticking to direct path->class mapping and implementing your own autoloader like this:
class MyLib_Autoloader
{
public function __construct() {
spl_autoload_register(array($this, 'autoload'));
}
public function autoload($className)
{
if (substr($className, 0, 6) == 'MyLib_') {
include dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR strtr($className, '_', DIRECTORY_SEPARATOR) . '.php';
return true;
}
return false;
}
}
This will check if the class is from your namespace and load the file. Assumes your autoloader is in /some/path/MyLib/ -> the include path will be /some/path/MyLib/../MyLib/ClassName.php. You can also add more logic to strip the initial MyLib from the class name and get rid of that '..'.
Make sure to use only require. It's faster and the autoload function is not called more than once for each classname! :) No need to include_once/require_once/etc. Go with the fastest.
Usage is as simple as:
include '/some/path/MyLib/Autploader.php';
$loader = new MyLib_Autoloader();

The main question is : who/how will your library be used ?
If that is by you only, then use Zend's autoloader, this will save you time. You don't need to reinvent the wheel.
If you have to make you library public and used on different projects, then that may be a problem because it will force the users of your library to have Zend Framework too. So in that case, either you make your own autoloader, either you pick one of a framework/library but include it in your library (watch out for the licenses).
Now regarding the utilisation of the autoloaders : only the autoloaders that are registered will be called. If you have your own autoloader, but you didn't mention how to setup it in your documentation, the users of your code will never think of set it up, and then it won't be used.
So the basic answer is to say : use many autoloaders, register them all, and they will be all called and everything will work fine. But that may lead to conflicts, because one autoloader will try to load something that is supposed to be handled by another autoloader, so you have to be careful with that, and not abuse of them.

Related

What is the right way to load dependencies in PHP?

I have a doubt about the right way/best practice about loading dependent classes in PHP.
I usually put all dependencies in the beginning of each class with a include_once in a way similar to Java imports. Something like:
include_once 'dto/SomeObjectDTO.php;'
include_once 'dao/SomeObjectDAO.php;'
include_once 'util/SomeObjectUtil.php;'
class SomeObjectService{
#class code here
}
This is the best way to load classes? Or maybe load all classes in a Bootstrap.php? Other ways?
Note that I'm talking about loading my own classes, not complex external classes like frameworks.
Like Homer6 said, autoloading is a php's built in dependency loading mechanism.
PHP-FIG proposed a family of PHP coding standards called PSR. PSR-0 deals with class naming and autoloading. Here are some links:
Requirements and an example autoloader
A good article on the subject
Also, keep in mind, that autoloading comes with a price. There is a lot of string work and work with the fs in the proposed default autoloader(you can implement your own faster autoloader, but it is not going to conform to the standard). This makes autoloading slow when you need to load a lot of classes. So if you needed to load 2 classes only, your approach would be faster and more understandable.
Since version 5.3 PHP supports namespaces.
This allows you to have a package and class hierarchy just like you know them from C++ or Java.
Check out those resources to learn more:
http://www.php.net/manual/en/language.namespaces.basics.php
http://php.net/manual/en/language.namespaces.importing.php
PHP's you can register your autoload method. Symfony 2 contains a nice class to do it with.
http://php.net/manual/en/function.spl-autoload-register.php
I've adapted it to work with the library that we've written.
https://github.com/homer6/altumo/blob/master/source/php/loader.php
https://github.com/homer6/altumo/blob/master/source/php/Utils/UniversalClassLoader.php
This adaption allows you to have namespaces that do not require the top level namespace to have the same folder name.
set_include_path(get_include_path()
. PATH_SEPARATOR . 'path1'
. PATH_SEPARATOR . 'path2'
);
// auto load classes:
function autoloadClasses($className) {
require_once $className . '.php';
}
spl_autoload_register('autoloadClasses');

PHP AWS SDK autoloader conflicts with Codeigniter

I'm using Codeigniter 2.1.0.
I'm including the Amazon Web Services SDK in a custom model using require_once APPPATH . "/libraries/aws_sdk/sdk.class.php";
This works well. However when CI decides it later needs to load it's db cache class, it calls (via CI_DB_driver::_cache_init):
if ( ! class_exists('CI_DB_Cache'))
{
if ( ! #include(BASEPATH.'database/DB_cache.php'))
{
return $this->cache_off();
}
}
This triggers the autoload of the AWS SDK (the exact method being CFLoader::autoloader).
I can get around this by telling class_exists not to autoload, as it seems that DB_cache.php is included anyway if class_exists returns false:
if ( ! class_exists('CI_DB_Cache', false))
{
if ( ! #include(BASEPATH.'database/DB_cache.php'))
{
return $this->cache_off();
}
}
This dirty fix works, but obviously only fixes the immediate problem. In general, what is the best way to ensure that CodeIgniter doesn't get confused with the autoload from other libraries?
Please note:
I've read bits and pieces about using spl_autoload_register. It seems that Codeigniter doesn't use this and I'm not sure how I should implement this safely. I would find a solid example of how and where I should implement this most useful (if, of course, this is the solution).
It appears that the AWS SDK already uses spl_autoload_register: spl_autoload_register(array('CFLoader', 'autoloader'));
As you probably know, you don't really want to be modifying the core methods in CI. So, to prevent CI from conflicting with your class, you want to do something very similar to the following:
Leave your folder structure as is, but create a new file outside your aws_sdk folder. Name it something like *Aws_loader* or something that makes sense to you. If you want to autoload the sdk, then in the CI autoloader file, add:
CI Autoload file (application/config/autoload.php):
$autoload['libraries'] = array('Aws_loader');
Your init file:
class CI_Aws_sdk{
// for use with PHP < version 5
/*public function CI_Aws_sdk(){
require dirname(__FILE__) . DIRECTORY_SEPARATOR . "aws_sdk" .DIRECTORY_SEPARATOR . 'sdk.class.php';
}*/
// for use with PHP >= 5
public function __construct(){
require dirname(__FILE__) . DIRECTORY_SEPARATOR . "aws_sdk" .DIRECTORY_SEPARATOR . 'sdk.class.php';
}
}
So your directory structure looks like this now:
application --
config --
...
libraries --
Aws_loader.php
aws_sdk --
sdk.class.php
If you aren't autoloading the sdk, then in your controller, you can do this:
$this->load->library('Aws_loader');
Either way, CI with load the class for you and effectively separate any methods within it and now you can operate within that class just like any other library or model that you've loaded previously, without interfering with CI's methods, similar to this:
$this->Aws_loader->do_something();
You can use the same method for any third party class library or even one that you wrote yourself. A very similar arrangement can be used for models, libraries, helpers and the like.
Hope this helps!

Autoload classes with Zend based naming convention or no convention at all with Symfony 2

How can I use the Symfony2 ClassLoader to autoload classes that dont follow PEAR naming conventions and don't use namespaces?
It seems from the symfony2 docs that this isn't possible. How does one include API helper libraries or other files that don't follow these types of conventions (i.e. they instead follow Zend conventions or no naming conventions at all)?
You should register autoloading functions for those classes using spl_autoload_register(). This will let you have several autoloaders rune one after another, until one manages to load requied class.
Code example of what Mchl said
in app/autoload.php
...
$loader->register();
...
/////////////////////////////////////////////////////////////////////////////////
// Own autoloader for non-namespaced nor PEAR lib. Replace tcpdf with yours.
function myLoader()
{
$file = __DIR__ . '/../vendor/tcpdf/tcpdf.php';
if (!file_exists($file))
{
return false;
}
require_once $file;
}
spl_autoload_register('myLoader');

Exception Based Class Loading in PHP

So I have an idea, but I'm thinking I need to run it by StackOverflow before I do something stupid.
I want to have an associative array of class names => filepaths. If PHP ever runs into a fatal error where the class is not defined, it will check if the key exists in my array and then require_once the class. This will prevent unnecessary bulk loading of classes that may never be used.
Bad idea?
How about trying PHP's built in autoloading.
Autoloading is the right way to do it, but spl_autoload_register is a cleaner way than __autoload, because it allows multiple autoloaders. Function __autoload also AFAIK stops working when spl_autoload_register is called, unless __autoload is also registered.
You can write your own autoload or use an existing one. For example, Zend Framework has an autoloader that uses conventions (Foo_Bar is in Foo/Bar.php). Nette Framework has RobotLoader, that indexes your classes and uses the index when neccessary. However, unless you use other things from the framework, it is probably too large.
see: http://www.php.net/manual/en/function.spl-autoload-register.php
If you are on PHP5, you can use __autoload().
makes your code a bit more manageable , although performance-wise, it's a bad choice. But I wouldn't worry it unless I'm building a Facebook.
What you are trying to do is already handled by the php __autoload function. You can read all about it here: http://php.net/manual/en/language.oop5.autoload.php
So, not a bad idea at all ;)
you should use autoloading with specified clas name structure, here is an example
the class names should should be only alpha and _ case-insensitive.
Lets take this directory structure and files
/classes/class.php
/classes/input/input.php
/classes/output/output.php
/classes/regex/regex.php
/classes/interface/parser/interface_parser.php
/classes/parser/parser.php
/classes/parser/xml/parser_xml.php
/classes/parser/html/parser_html.php
having the structure like this is good as it encourages you to code better when it comes to OOP.
Now if we take a look at the /classes/parser/html/html_parser.php file:
class Parser_Html extends Parser implements Interface_Parser
{
//looks nice in here
}
usually you would have to make sure the interface and the extended class is loaded, but these get autoloaded as well if they have not already.
creating the auto load system for this is not that complex, its just 1 function.
function __autoload($name)
{
//Classes
$parts = explode('_',strtolower($name));
$path = '/classes/';
foreach($parts as $p)
{
$path .= $p;
}
$path .= '/' . $name . '.php';
if(file_exists($path))
{
require_once $path;
}
}
so instead of including the class file first just run the class initiation.
$HtmlParser = new Parser_Html();
as the file has not been include the __autoload is run with a param of the class name, the autoload then looks in the directory that's relevant to the class name to try and load it.
also as your using the extend keyword in the class file shown above the class that is to be the parent gets run threw trhe autoloader aswell so you do not need to pre-load interfaces and classes etc.
Hope this helps you.
Note:
All code provided is untested and written for informational purposes, I would recommend you research the techniques more in detail before any implementation is done.

ZF Autoloader to load ancestor and requested class

I am integrating Zend Framework into an existing application. I want to switch the application over to Zend's autoloading mechanism to replace dozens of include() statements.
I have a specific requirement for the autoloading mechanism, though. Allow me to elaborate.
The existing application uses a core library (independent from ZF), for example:
/Core/Library/authentication.php
/Core/Library/translation.php
/Core/Library/messages.php
this core library is to remain untouched at all times and serves a number of applications. The library contains classes like
class ancestor_authentication { ... }
class ancestor_translation { ... }
class ancestor_messages { ... }
in the application, there is also a Library directory:
/App/Library/authentication.php
/App/Library/translation.php
/App/Library/messages.php
these includes extend the ancestor classes and are the ones that actually get instantiated in the application.
class authentication extends ancestor_authentication { }
class translation extends ancestor_translation { }
class messages extends ancestor_messages { }
usually, these class definitions are empty. They simply extend their ancestors and provide the class name to instantiate.
$authentication = new authentication();
The purpose of this solution is to be able to easily customize aspects of the application without having to patch the core libraries.
Now, the autoloader I need would have to be aware of this structure. When an object of the class authentication is requested, the autoloader would have to:
1. load /Core/Library/authentication.php
2. load /App/Library/authentication.php
My current approach would be creating a custom function, and binding that to Zend_Loader_Autoloader for a specific namespace prefix.
Is there already a way to do this in Zend that I am overlooking? The accepted answer in this question kind of implies there is, but that may be just a bad choice of wording.
Are there extensions to the Zend Autoloader that do this?
Can you - I am new to ZF - think of an elegant way, conforming with the spirit of the framework, of extending the Autoloader with this functionality? I'm not necessary looking for a ready-made implementation, some pointers (This should be an extension to the xyz method that you would call like this...) would already be enough.
To extend what Gordon already pointed out, I'd create my own autoloader class that implements Zend_Loader_Autoloader_Interface and push that onto the Zend_Loader_Autoloader-stack.
class My_Autoloader implements Zend_Loader_Autoloader_Interface
{
public function autoload($class)
{
// add your logic to find the required classes in here
}
}
$autoloader = Zend_Loader_Autoloader::getInstance();
// pushAutoloader() or unshiftAutoloader() depending on where
// you'd like to put your autoloader on the stack
// note that we leave the $namespace parameter empty
// as your classes don't share a common namespace
$autoloader->pushAutoloader(new My_Autoloader(), '');
I wouldn't go with the Zend_Loader approach as, even though not deprecated yet, the new Zend_Loader_Autoloader seems to be best practice currently.
See the manual on Zend_Loader:
By default, the autoloader is configured to match the "Zend_" and "ZendX_" namespaces. If you have your own library code that uses your own namespace, you may register it with the autoloader using the registerNamespace() method.
$autoloader->registerNamespace('My_');
Note that the autoloader follows the ZF Naming Convention, so Zend_Foo_Bar will look in Zend/Foo/Bar.php.
However,
You can also register arbitrary autoloader callbacks, optionally with a specific namespace (or group of namespaces). Zend_Loader_Autoloader will attempt to match these first before using its internal autoloading mechanism.
$autoloader->pushAutoloader(array('ezcBase', 'autoload'), 'ezc');
Another way would be to create custom class Loader extending Zend_Loader and then register it with:
Zend_Loader::registerAutoload('My_Loader');
ZF will then use this autoloader instead of the default one.

Categories