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

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.

Related

Assign functions from another file to a Class

I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".

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.

Dynamic class name in PHP

I'm trying to create a system that it has a GeneralObj. The GeneralObj allows user to initiate special objects for database's tables with a String of the table name. So far, it works perfect.
class GeneralObj{
function __construct($tableName) {
//...
}
}
However, it is too tired to type new GeneralObj(XXX) every time.
I am wondering is that possible to simplify the process to like new XXX(), which is actually running the same as new GeneralObj(XXX)?
I spot PHP provided __autoload method for dynamic loading files in the setting include_path but it requires a the actually definition file existing. I really don't want to copy and copy the same definition files only changing a little.
For cause, eval is not an option.
Maybe you can just auto-create the files in the autoloader:
function __autoload($class_name) {
// check for classes ending with 'Table'
if (preg_match('/(.*?)Table/', $class_name, $match)) {
$classPath = PATH_TO_TABLES . '/' . $match[1] . '.php';
// auto-create the file
if (!file_exists($classPath)) {
$classContent = "
class $class_name extends GeneralObj {
public __construct() {
parent::__construct('{$match[1]}');
}
}";
file_put_contents($classPath, $classContent);
}
require_once $classPath;
}
}
Use inheritance. Make GeneralObj the superclass of the table specific classes. This way you can dynamically derive class names and instantiate objects. Example:
class someTable extends GeneralObj {
}
$tableName = 'some';
$className = $tableName . 'Table';
$obj = new $className;
No, this is not possible.
The runkit extension allows programmatic manipulation of the PHP runtime environment, but it cannot do this. Even if it could, it would IMHO be a very bad idea, greatly impacting the requirements and complexity of the application in exchange for saving a few keystrokes.
In an unrelated note, your GeneralObj class has functionality that sounds suspiciously like that of a dependency injection container. Perhaps you should consider replacing it with one?
Something like this autoloader:
myAutoloader::Register();
class myAutoloader
{
/**
* Register the Autoloader with SPL
*
*/
public static function Register() {
if (function_exists('__autoload')) {
// Register any existing autoloader function with SPL, so we don't get any clashes
spl_autoload_register('__autoload');
}
// Register ourselves with SPL
return spl_autoload_register(array('myAutoloader', 'Load'));
} // function Register()
/**
* Autoload a class identified by name
*
* #param string $pClassName Name of the object to load
*/
public static function Load($pClassName){
if (class_exists($pClassName,FALSE)) {
// Already loaded
return FALSE;
}
$pClassFilePath = str_replace('_',DIRECTORY_SEPARATOR,$pClassName) . '.php';
if (file_exists($pClassFilePath) === FALSE) {
// Not a class file
return new GeneralObj($pClassName);
}
require($pClassFilePath);
} // function Load()
}
And it's up to GeneralObj to throw an exception if the table class can't be instantiated

Lazy load class in PHP

I want to lazy load class but with no success
<?php
class Employee{
function __autoload($class){
require_once($class);
}
function display(){
$obj = new employeeModel();
$obj->printSomthing();
}
}
Now when I make this
function display(){
require_once('emplpyeeModel.php');
$obj = new employeeModel();
$obj->printSomthing();
}
It works but I want to lazy load the class.
__autoload is a standalone function not a method of a class. Your code should look like this:
<?php
class Employee{
function display(){
$obj = new employeeModel();
$obj->printSomthing();
}
}
function __autoload($class) {
require_once($class.'.php');
}
function display(){
$obj = new Employee();
$obj->printSomthing();
}
UPDATE
Example taken from the php manual:
<?php
function __autoload($class_name) {
include $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
Change Employee a bit:
class Employee {
public static function __autoload($class) {
//_once is not needed because this is only called once per class anyway,
//unless it fails.
require $class;
}
/* Other methods Omitted */
}
spl_autoload_register('Employee::__autoload');
First if all it's better to use spl_autoload_register() (check the note in php's manual for autoloading).
Then back to your problem; only if the display() function is in the same directory as the employeeModel this will work. Otherwise, use absolute paths (see also include() and include_path setting

Handling Global Variables between different Classes and Functions in PHP

What is the best way to make a variable accessable to all classes.
For example, I want to have a configuration file (Call it config.php) that is going to have a variable like so:
$server_url = "www.myaddress.com";
And I have a main library type file that contains a bunch of classes that need to access the $server_url. So here begins that main library file:
require 'config.php';
class one {
function a() {
$html = "<a href='$server_url/main.php'>LINK</a>"
return $html;
}
function b() {
$html = "<a href='$server_url/about.php'>LINK</a>"
return $html;
}
}
class two {
function new() {
$html = "<a href='$server_url/blah.php'>LINK</a>
}
}
What would be the best way to make $server_url from the config.php available to every function? Or at least available to all the functions in a class?
Personally I would use a static entity to hold all configuration values.
Usually, most php applications have a single entry point (index.php) that can load up the config.php file and make the static entity available from that point.
If your application has multiple entry points, then you will need to include config.php in each of these points.
Something like this:
<?php
class Configurator{
private static $_configuration = array();
public static function write($key, $value) {
self::$_configuration[$key] = $value;
}
public static function read($key) {
return self::$_configuration[$key];
}
}
Configurator::write('server', 'http://localhost');
Configurator::read('server');
?>
CakePHP has a similar class: http://api.cakephp.org/view_source/configure/
Make the config into a class in itself and use a static methods either along the line of serverUrl() or get('server_url'). Then call them like any other static methods to classes (I'll choose the latter in this example):
$html = "<a href='" . Config::get ('server_url') . "/main.php'>LINK</a>";
The config class could be pretty slim, use a constructor like:
public function __construct (array $config)
{
foreach ($config as $key => $value)
{
$this->$key = $value;
}
}
And add the get() method along these lines:
public function get ($key)
{
return $this->$key;
}
This way you can read the config from an array that you can have as a separate, actual config file, and reuse the same code for multiple projects.
You'll also be able to access the variables from anywhere in the project and you'll get a sort of pseudo-namespacing (in case the project needs to run on an older version of PHP).
Please, don't copy the code verbatim, it's written as an example.
think of globals are evil. Try to use design patterns to get access to some configs globally.
I'm a big fan of singletons to get global access to objects, arrays or other data-types.
<?php
class st {
static $_this;
function __construct(){
self::$_this = $this;
}
static function &getInstance(){
return self::$_this
}
static function set($key, $value){
self::$_this[$key] = $value;
}
static function &get($key){
return self::$_this[$key];
}
}
// Usage
new st();
st::set('foo', 'bar');
// In some class
st::get('foo'); //return 'bar'
// Or when there are some classes/objects
st::getInstance()->foo->bar();
$st =& st::getInstance();
$st->foo->bar();
?>
Roughly wrote down a small singleton, but don't know whether there is a syntax error.
While handling with getInstance it's certain that you define the variable by reference =&
Define a constant in config.php like:
define('SERVER_URL', '...');
In your class:
echo SERVER_URL;
What works for me the best is to use a config file like config.ini
And then to use $my_config = parse_ini_file(file path/config.ini');
Now everywhere in my code including inside functions and classes, I will use
The PHP superglobal like this:
$GLOBALS["my_config"]['my_global_var']

Categories