Php __autoload() function, how to use - php

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();

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).

Accessing classes from other classes and in functions PHP

I've not written OOP code before, I always tried to avoid the extra keystrokes. Anyway, I'm taking the plunge as it's supposed to increase readability.
Anyway, I'm having trouble getting my classes to be able to access each other's methods.
Here's my setup:
$conf = new Conf(); // boot up!
// Include dependencies, system files and other functions
require_once $conf->getIncludePath() . 'error.php';
require_once $conf->getIncludePath() . 'pool.php';
require_once $conf->getIncludePath() . 'database.php';
require_once $conf->getIncludePath() . 'api.php';
require_once $conf->getIncludePath() . 'user.php';
require_once $conf->getIncludePath() . 'forms.php';
require_once $conf->getIncludePath() . 'page.php';
require_once $conf->getIncludePath() . 'resources.php';
$error = new Error();
$pool = new Pool();
$db = new Database();
$api = new Api();
$user = new User();
$forms = new Forms();
$page = new Page();
$resources = new Resources();
My question is, how do I get it so that a method in User class can run the query method inside Database, to say fetch their information?
I'm aware of using global $db; global $user; etc. inside every method ever, but isn't there a way for me to get to these variables without having to essentially redeclare them every time I want to use them?
Thanks
Piers
Using objects is so much more than just readability.
Without going into a full blown explanation of the reasons behind this, I highly recommend further research into the topic. Starting with why they are used and the problems that they resolve (and cause!).
As for your question:
You have a large number of objects that should really be able to 'talk' to each other.
We could say that the User class is dependent on the Database. To resolve this dependency we need to be able to reference the Database in the context on the User (the correct scope).
There are a few ways to accomplish this, however the common practice would be to define a member variable inside the User class and the set the value of the property using setters and getters. This is known to be injecting the dependencies.
For example:
class User {
protected $database;
public function getDatabase()
{
return $this->database;
}
public function setDatabase(Database $database) {
$this->database = $database;
}
public function getUsers()
{
$sql = 'SELECT * FROM user';
return $this->getDatabase()->execute($sql);
}
}
Your client code would then become:
$db = new Database();
$user = new User();
$user->setDatabase($db);
Please note that the above example is a contrived one at best - Making the User class depending on the Database would be a design decision that will cause you other problems further down the line - Something I think you need to research into.
Use dependency injection!
You could create a new class, DBHandler, that stores an instance of your Database class into every other class you want.
For example:
class DBHandler {
public static $db;
public static function init($db) {
self::$db = $db;
}
}
$db = new Database();
DBHandler::init($db);
Then you just need to inherit from this class, by using the extends keyword.
i.e.:
class User extends DBHandler {
// ... your code here ...
}
This is an example of how you could implement the dependency injection.
The __autoload() function
You don't need to require a class everytime you need if you use the __autoload() function.
For example, you can do this to automatically resolve dependencies:
function __autoload($classname) {
require_once $classname . ".php";
}
Following the tutorial at nettuts+ lead me to write this:
// load configuration
require_once 'includes/class.conf.php';
$conf = new Conf(dirname(__FILE__));
// dependency class
$conf->registerRequiredFile('dependencies');
$dependencies = new Dependencies();
// start registering dependencies
$conf->registerRequiredFile('error');
$error = $dependencies->error = function(){ // error
$error = new Error();
$error->conf = $conf;
return $error;
};
$conf->registerRequiredFile('pool');
$pool = $dependencies->pool = function(){ // error
$pool = new Pool();
$pool->db = $db;
$pool->user = $user;
$pool->error = $error;
// etc. any other dependencies
return $pool;
};
It works perfectly, the first conf file contains the registerRequiredFile method that then require_once a uri based on the parameter given.
Thanks for your help in pointing me in the right direction!

How can i manage my dependencies?

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';
});
?>

Loading PHP classes from string

I'm looking for a way to load classes into PHP without using hardwired names.
The idea is the script will load a text file with names of 'components'(classes), then load them by the names in the file. For example:
<xml><Classes><class name="myClass"/></Classes></xml>
When the PHP is run, it would need to do something like this:
require_once {myClass}".class.php";
var myclass = new {myClass}();
require_once $class . ".class.php";
$myclass = new $class;
See http://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new.
Your example is almost correct as is. You can just replace {myClass} with $myClass and it should work.
Here's a simple example of how this could be used:
File: myClass.class.php
<?php
class myClass {
public $myVar = 'myText';
}
?>
File: test.php
<?php
$className = "myClass";
require_once $className . ".class.php";
$myInstance = new $className;
echo $myInstance->myVar;
?>
This should output "myText" to the screen, the contents of your dynamically included class property.
Use the ReflectionClass for this.
require_once $class . ".class.php";
$refl = new \ReflectionClass($class);
$params_for_construct = array($param1, param2);
$instance = $refl->newInstanceArgs($params_for_construct);
Why not just use autoloader
spl_autoload_register(function ($class) {
require 'class_folder/' . $class . '.class.php';
});

How can I call a static method on a variable class?

I'm trying to make some kind of function that loads and instantiates a class from a given variable. Something like this:
<?php
function loadClass($class) {
$sClassPath = SYSPATH."/classes/{$class}.php";
if (file_exists($sClassPath)) {
require_once($sClassPath);
$class = $class::getInstance();
}
}
?>
If I use it like this:
<?php
loadClass('session');
?>
It should include and instantiate the session class.
BTW: the static getInstance function comes from this code:
<?php
function getCallingClass() {
$backtrace = debug_backtrace();
$method = $backtrace[1]['function'];
$file = file($backtrace[1]['file']);
$line = $file[($backtrace[1]['line'] - 1)];
$class = trim(preg_replace("/^.+?([A-Za-z0-9_]*)::{$method}\(.*$/s", "\\1\\2", $line));
if(! class_exists($class)) {
return false;
} return $class;
}
class Core {
protected static $instances = array();
public static function getInstance() {
$class = getCallingClass();
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
} return self::$instances[$class];
}
}
?>
The thing is that right now the way to use the functions in a class is this:
<?php
$session = session::getInstance();
?>
But now I want to build that into a function so that I never again have to use that line of code.
I just say loadClass('session');
and than I can use $session->blablablafunction();
Calling static functions on a variable class name is apparently available in PHP 5.3:
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod(); // As of PHP 5.3.0
http://php.net/manual/en/language.oop5.static.php
Could definitely use that right now myself.
Until then you can't really assume that every class you are loading is designed to be a singleton. So long as you are using < 5.3 you'll have to just load the class and instantiate via the constructor:
function loadClass($class) {
$sClassPath = SYSPATH."/classes/{$class}.php";
if (file_exists($sClassPath)) {
require_once($sClassPath);
$class = new $class;
}
}
OR
Just load the class without creating an object from it. Then call "::getInstance()" on those meant to be singletons, and "new" on those that are not, from outside of the loadClass() function.
Although, as others have pointed out earlier, an __autoload() would probably work well for you.
You can use call_user_func():
$class = call_user_func(array($class, 'getInstance'));
The first argument is a callback type containing the classname and method name in this case.
Why not use __autoload() function?
http://www.php.net/autoload
then you just instantiate object when needed.
It looks like you are fighting PHP's current implementation static binding, which is why you are jumping through hoops with getCallingClass. I can tell you from experience, you should probably abandon trying to put instantiation in a parent class through a static method. It will cause you more problems in the end. PHP 5.3 will implement "late static binding" and should solve your problem, but that obviously doesn't help now.
You are probably better off using the autoload functionality mentioned by kodisha combined with a solid Singleton implementation. I'm not sure if your goal is syntactic sugar or not, but it think you will do better in the long run to steer clear of trying to save a few characters.
Off the top of my head, needs testing, validation etc:
<?php
function loadClass($className) {
if (is_object($GLOBALS[$className]))
return;
$sClassPath = SYSPATH."/classes/{$className}.php";
if (file_exists($sClassPath)) {
require_once($sClassPath);
$reflect = new ReflectionClass($className);
$classObj = $reflect->newInstanceArgs();
$GLOBALS[$className] = $classObj;
}
}
?>
Late static bindings will work for you I think. In the construct of each class do:
class ClassName
{
public static $instances = array();
public function __construct()
{
self::$instances[] = $this;
}
}
Then...
Here is an autoloader I created. See if this solves your dilemma.
// Shorten constants for convenience
define ('DS', DIRECTORY_SEPARATOR);
define ('PS', PATH_SEPARATOR);
$template = "default";
// Define an application path constants
define ('APP_ROOT', realpath('.').DS);
define ('VIEW', APP_ROOT . 'Views' . DS);
define ('MODEL', APP_ROOT . 'Models' . DS);
define ('CONTROLLER', APP_ROOT . 'Controllers' . DS);
define ('TEMPLATE', VIEW."templates".DS.$template.DS);
define ('CONTENT', VIEW."content".DS);
define ('HELPERS', MODEL."helpers".DS);
// Check if application is in development stage and set error reporting and
// logging accordingly
error_reporting(E_ALL);
if (defined('DEVELOPMENT')) {
ini_set('display_errors', 1);
} else {
ini_set('display_errors', 0);
ini_set('log_errors', 'On');
ini_set('error_log', APP_ROOT.'error.log');
}
$paths = array(APP_ROOT, VIEW, MODEL, CONTROLLER, TEMPLATE, CONTENT, HELPERS);
// Set the include path from Config Object
set_include_path(implode(PS, $paths));
// Autoloader
function __autoload($class)
{
require_once $class.'.php';
return;
}
Then all you have to do is
$var = new ClassName();
but you have to have a php file in the path with the name
ClassName.php
where ClassName is the same as the name of the class you want to instantiate.

Categories