How can i manage my dependencies? - php

i'm working on a likely a large PHP project where i have too many classes, and my problem appears when some classes depends on others, for example the error-handling class may depend on the security class, the user class may depend on the database class and so on...
$obj1 = new class1();
$obj2 = new class2($obj1);
$obj3 = new class3();
$obj4 = new class4($obj3 , $obj1);
etc...
hence my quesion comes in! what is the best way to manage dependancies ?

Try autoloading your classes. That way if a class needs another class, it doesn't have to require() it.
http://php.net/manual/en/function.spl-autoload-register.php
Example from the documentation:
<?php
function my_autoloader($class) {
include 'classes/' . $class . '.class.php';
}
spl_autoload_register('my_autoloader');
// Or, using an anonymous function as of PHP 5.3.0
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.class.php';
});
?>

Related

PHP OOP - Autoinitiate objects

<?php function __autoload($class_name)
{
include_once 'inc/classes/class.' . $class_name . '.inc.php';
}
?>
Right now im using __autoload to automatic load up my classes whenever they are used. But i was thinking, why not automatic initiate the object themself as well, so you dont have to start the object in the pages itself, you could just call the properties of an class without starting the object.
But here i am stuck, i thought i could just do like the example below but its not working, objects are not starting.
<?php
function __autoload($class_name)
{
include_once 'inc/classes/class.' . $class_name . '.inc.php';
'$'.$class_name = new $class_name;
}
?>
This does not make sense. As you say, autoloading happens when the class is referenced.
How is it referenced? Let's see:
With $object = new TheClassName() - there you already have your instance. Why create another one automagically?
With static method calls, access to static properties or constants TheClassName::I_NEED_THIS_CONSTANT - why would I need an automagically created instance if I access a static method/property/constant?
With calls to class_exists() - why would I need an automagially created instance if I just want to check if the class exists?
Try this:
$$class_name = new $class_name();
You can also try using {}. In your example it will be:
${$class_name} = new $class_name;
With ${} you can create dynamic variables like that:
$i = '1';
${'tmp' . $i} = 'Hello world';
echo $tmp1; // Hello world
You should also use spl_autoload_register instead of __autoload (because it can be deprecated or removed in the future).

Php __autoload() function, how to use

I just started learning PHP from a book. In the OOP chapter there is an explanation of the __autoload() function for "Automatically Load Class Files" but my book doesn't say where I should declare this function.
I tried to Google it and in the PHP documentation but I really can't find where I should declare this function. In "global scope" (I don't know if it is the same as JavaScript)? Inside the class that should be autoloaded? Or in the "local scope" of the class where I have to load the class?
The way of using the __autoload() function is :
<?php
function __autoload($class){
if(file_exists($class . ".php")){
require_once $class . ".php";
}
}
$class1 = new Class1();
$class2 = new Class2();
On the very top of your page declare the function than you can start using it just like in the example below .
If you want it to be available site-wide than consider making a new file type the code there and include that file in the top of your page.
something.php
<?php
function __autoload($class){
if(file_exists($class . ".php")){
require_once $class . ".php";
}
}
Than just include something.php on all the pages you need like :
<?php
require_once 'something.php';
$class1 = new Class1();
$class2 = new Class2();

Autoload only registers one class at a time

I have this autoloader which loads only one class at a time. I can't figure out what is wrong with it.. I initially made to learn as much about PSR-0 as possible, though according to code review I did everything required, but it just won't load two different files, from different namespaces as seen below.
class Autoloader
{
private $pathToClass;
//register the path
function __construct($pathToClass)
{
$this->pathToClass = $pathToClass;
}
//load the file
public function load($class)
{
// expload the namespaces ex: foo\bar\tar array(foo, bar, tar)
$explode = explode('\\', $class);
//get the last exploaded string and append .php so it becomes tar.php
$class = $explode[count($explode) - 1].'.php';
// required tar.php in the path it is found ex:
// require '/foo/bar/tar.php' in lowecase to avoid windows/unix conflict
if(file_exists(strtolower($this->pathToClass.$class))){
require strtolower($this->pathToClass.$class);
return true;
}
return false;
}
// autoload
public function register()
{
spl_autoload_register([$this, 'load']);
}
}
Here is how it is instantiated:
$myLibLoader = new Autoloader(__DIR__.'/foo/bar/');
$myLibLoader->register();
$foo = new foo();
EDIT
The above was the first autoloader I had created, but below I am showing the improved autoloader, which which I have the same problem.
<?php
class AutoloaderException extends Exception{}
class AutoLoader
{
private $classDir;
private $namespace;
public $dirSeparatorSymbol = '\\';
public function __construct($namespace, $classDir)
{
$this->classDir = $classDir;
$this->namespace = $namespace;
}
private function load($class)
{
$include_path = str_replace($this->dirSeparatorSymbol, DIRECTORY_SEPARATOR, $this->classDir);
$classFilename = strtolower(substr($class, strrpos($class, '\\') + 1) . '.php');
if(file_exists($include_path.$classFilename)){
require $include_path.$classFilename;
return true;
}
throw new AutoloaderException('Class '.$classFilename. ' could not be loaded');
}
public function register()
{
spl_autoload_register([$this, 'load']);
}
}
/* INITIALIZING The autloader */
$b = new Autoloader('mercury\venus\earth', __DIR__.'/mercury/venus/earth/');
$b->register();
$a = new Autoloader('bar\tar', __DIR__.'/foo/bar/tar/');
$a->register();
$x = new bar\tar;
$y = new mercury\venus\earth;
I think the crux of your issue was identified in your original CodeReview question, specifically this comment:
You still don't construct a path from the class qualifier passed to
load(). As the question how to do this is offtopic on CodeReview,
consider posting on StackOverflow (with a clear explanation on the
parts you don't understand). – #ComFreek
I think, given two classess with no namespace, your autoloader as-is should perform fine. Once you introduce namespaces though you're going to have an issue because you're not taking it into account at all.
Namespaces are important in PSR-0 because they contain further path information. For example, you may want all of your classes to live in /some/directory, but if you add namespaces to those files (extending your example using the \Foo\Bar\Tar class) then according to PSR-0 that class must be declared in the file /some/directory/Foo/Bar/Tar.php.
Taking this into consideration, these lines must be changed in your autoloader in order for it to become PSR-0 compliant:
if(file_exists(strtolower($this->pathToClass.$class))){
require strtolower($this->pathToClass.$class);
return true;
}
Specifically, the filepath of the class file you're trying to require should be something like this:
$this->pathToClass . DIRECTORY_SEPARATOR . implode('/', $class) . '.php';
(That is, assuming you take this line out: $class = $explode[count($explode) - 1].'.php'; )
I'm not sure if you've had the chance, but this article on Site Point is a great read and covers pretty much everything you need to know regarding PSR-0 and autoloading.

zend framework2 how does the autoload function work

recently I was learning zend framework 2, and there's a problem annoying me for a long time, things look like this:
<?php
namespace Album\Model;
// Add these import statements
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface
{
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
// Add content to these methods:
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
//....
?>
This code was a section of the "skeleton application" programme, which was a tutorial of ZF2. The first time I see the programme, I don't understand what's the usage of "namespace" and "use", because this two keyword doesn't exist in php5.2(also the same in the earlier edition), so I go to see the manual and try to understand it.I write a programme to simulate what really happens:
<?php
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the programme above works well, of course I created two folders named script and lib, and there's a file named test.php.
Seems like every thing is clear, zend framework also has a autoload function, BUT when I noticed the codes in "skeleton application programme", there was a namespace in the beginning, so I adds the namespace to my programme too:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
the page returned me inforamtion as following:
Fatal error: Class 'script\lib\test' not found in E:\wamp\www\test\test_29.php on line 6
I tried to change the namespace's name such as script\lib, script\lib\test...
but it's useless.
Any answer will be appreciated, thanks.
Now I will give you more details about this issue:
To understand the usage of "namespace" and "use", I looked over the materials on php.net:
http://php.net/manual/en/language.namespaces.importing.php
In this page, there was a section of code looks like this:
Example #1 importing/aliasing with the use operator
<?php
namespace foo;
use My\Full\Classname as Another;
// this is the same as use My\Full\NSname as NSname
use My\Full\NSname;
// importing a global class
use ArrayObject;
$obj = new namespace\Another; // instantiates object of class foo\Another
$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // instantiates object of class ArrayObject
// without the "use ArrayObject" we would instantiate an object of class
?>
Now let's review the programme I write in the above:
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
function __autoload( $className ) {
$classname = strtolower( $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.php' );
}
?>
It's the same, I'm trying to simulate that instance, if we don't use the autoload function:
<?php
namespace test;
use script\lib\test;
require_once 'script/lib/test.php';
$o = new test();
echo $o->getWelcome();
?>
It works well too, BUT when I use __autoload function to load the class file, there's something wrong.
I don't konw where's problem, OR any body tried to write an instance to put the "Example #1" into practice? I will wait for your answer.
I think you're misunderstanding what's going on here.
Namespaces allow you to, more or less, create "directories" for your classes. So you can create the \Foo class and the \Test\Foo class (where \ represents the "root" of your application).
The way autoloading works is that your files mirror your namespacing. So foo.php would be in the root of your autoloading but you would create /test/foo.php for \Test\Foo
The use keyword has two uses. One is to alias class files and the other is, in PHP 5.4 or later, to bring in a Trait into your current class.
Now, to your question. First, Let's look at your code
<?php
namespace test;
use script\lib\test;
$o = new test();
echo $o->getWelcome();
This is confusing. You declare a namespace (which you don't need to do here) but then you alias it to script\lib\test. PHP is now looking for a file called /script/lib/test.php, which your error message says doesn't exist. But you said the file does exist so let's look at that
public function getWelcome() {
return 'welcome';
}
This isn't a class. It's a function. For this example you need a complete class
<?php
namespace script\lib;
class test {
public function getWelcome() {
return 'welcome';
}
}
Lastly, let's talk autoloading. You don't need to use use with autoloading. Your autoloader should take care of that for you. You should, however, use spl_autoload_register(), as __autoload() is soon to be depreciated.
From ZF2 docu
Zend\Loader\StandardAutoloader is designed as a PSR-0-compliant autoloader. It assumes a 1:1 mapping of the namespace+classname to the filesystem, wherein namespace separators and underscores are translated to directory separators.
Read more about: PSR-0
So if you're using namespaces the classname that gets send to the autoloader doesn't look like test. It looks like YOUR_NAMESPACE\test. YOUR_NAMESPACE is the namespace that you defined in the class with namespace YOUR_NAMESPACE;
PSR-0 is a standard that says: Your namespace should reflect your filesystem. You only have to replace the backslashes with forward slashes. Or _ with / if you're using pseudo namespaces like in ZF1. (Album_Model_Album)
So output the $className that is sent to your autoloader and you will see..

Autoloading both Classes & Models

I know it's common practice to autoload your controllers when using an MVC framework. I have made my own mini-framework where controllers are autoloaded fine.
Are there any security/bad issues with having the same autoload function load the models too?
I.e.
function __autoload($className) { // Autoload both controllers and models.
if(stristr($className, 'Model'))
{
if (is_readable(Ms . $className . '.php')) {
include Ms . $className . '.php';
}
} else {
if (is_readable(Cs . $className . '.php')) {
include Cs . $className . '.php';
}
}
}
You could use namespaces and spl_autoload_register() in order to get such an autoloader. There's no specific security issues regarding a multi autoloader (an autoloader for multi classes of classes) rather than a controller-only autoloader.
I usually works with namespaces like:
$home = new controller\home;
$home->actionIndex();
$users = new model\users;
$post = new view\post;
from there it's easy to replace a \ in the class name with a / to get the specific paths for the file (obviously doing the needed security checking as always).

Categories