Forget late static binding, I need late static __FILE__ - php

I'm looking for the get_called_class() equivalent for __FILE__ ... Maybe something like get_included_file()?
I have a set of classes which would like to know what directory they exist in. Something like this:
<?php
class A {
protected $baseDir;
public function __construct() {
$this->baseDir = dirname(__FILE__);
}
public function getBaseDir() {
return $this->baseDir;
}
}
?>
And in some other file, in some other folder...
<?php
class B extends A {
// ...
}
class C extends B {
// ...
}
$a = new A;
echo $a->getBaseDir();
$b = new B;
echo $b->getBaseDir();
$c = new C;
echo $c->getBaseDir();
// Annnd... all three return the same base directory.
?>
Now, I could do something ghetto, like adding $this->baseDir = dirname(__FILE__) to each and every extending class, but that seems a bit... ghetto. After all, we're talking about PHP 5.3, right? Isn't this supposed to be the future?
Is there another way to get the path to the file where a class was declared?

ReflectionClass::getFileName

Have you tried assigning it as a static member of the class?
<?php
class Blah extends A {
protected static $filename = __FILE__;
}
(Untested, and statics plus class inheritance becomes very fun...)

what if you don't use __FILE__ but a separate variable and set the variable to __FILE__ in each class
class A {
protected static $baseDir;
protected $filename = __FILE__; // put this in every file
public function __construct() {
}
public function getBaseDir() {
return dirname($this->filename) . '<br>'; // use $filename instead of __FILE__
}
}
require('bdir/b.php');
require('cdir/c.php');
class B extends A {
protected $filename = __FILE__; // put this in every file
}
$a = new A;
echo $a->getBaseDir();
$b = new B;
echo $b->getBaseDir();
$c = new C;
echo $c->getBaseDir();
you still have to redeclare the property in each class, but not the method

You could use debug_backtrace(). You probably don't want to, though.

Tested:
protected static $filename = __FILE__;
static::$filename
Doesn't work.
It looks like the constant is registered when the class is loaded which is not "late".
Not sure it was possible before, but what was best for me today is using reflection :
$basePath = new \ReflectionObject($this);
$dir = dirname($basePath->getFileName());

Related

PHP return the instanceof an object [duplicate]

public class MyClass {
}
In Java, we can get class name with String className = MyClass.class.getSimpleName();
How to do this in PHP? I already know get_class(), but it works only for objects. Currently I work in Active Record. I need statement like MyClass::className.
Since PHP 5.5 you can use class name resolution via ClassName::class.
See new features of PHP5.5.
<?php
namespace Name\Space;
class ClassName {}
echo ClassName::class;
?>
If you want to use this feature in your class method use static::class:
<?php
namespace Name\Space;
class ClassName {
/**
* #return string
*/
public function getNameOfClass()
{
return static::class;
}
}
$obj = new ClassName();
echo $obj->getNameOfClass();
?>
For older versions of PHP, you can use get_class().
You can use __CLASS__ within a class to get the name.
http://php.net/manual/en/language.constants.predefined.php
It sounds like you answered your own question. get_class will get you the class name. It is procedural and maybe that is what is causing the confusion. Take a look at the php documentation for get_class
Here is their example:
<?php
class foo
{
function name()
{
echo "My name is " , get_class($this) , "\n";
}
}
// create an object
$bar = new foo();
// external call
echo "Its name is " , get_class($bar) , "\n"; // It's name is foo
// internal call
$bar->name(); // My name is foo
To make it more like your example you could do something like:
<?php
class MyClass
{
public static function getClass()
{
return get_class();
}
}
Now you can do:
$className = MyClass::getClass();
This is somewhat limited, however, because if my class is extended it will still return 'MyClass'. We can use get_called_class instead, which relies on Late Static Binding, a relatively new feature, and requires PHP >= 5.3.
<?php
class MyClass
{
public static function getClass()
{
return get_called_class();
}
public static function getDefiningClass()
{
return get_class();
}
}
class MyExtendedClass extends MyClass {}
$className = MyClass::getClass(); // 'MyClass'
$className = MyExtendedClass::getClass(); // 'MyExtendedClass'
$className = MyExtendedClass::getDefiningClass(); // 'MyClass'
It looks like ReflectionClass is a pretty productive option.
class MyClass {
public function test() {
// 'MyClass'
return (new \ReflectionClass($this))->getShortName();
}
}
Benchmark:
Method Name Iterations Average Time Ops/second
-------------- ------------ -------------- -------------
testExplode : [10,000 ] [0.0000020221710] [494,518.01547]
testSubstring : [10,000 ] [0.0000017177343] [582,162.19968]
testReflection: [10,000 ] [0.0000015984058] [625,623.34059]
To get class name you can use ReflectionClass
class MyClass {
public function myNameIs(){
return (new \ReflectionClass($this))->getShortName();
}
}
Now, I have answer for my problem. Thanks to Brad for the link, I find the answer here. And thanks to J.Money for the idea. My solution:
<?php
class Model
{
public static function getClassName() {
return get_called_class();
}
}
class Product extends Model {}
class User extends Model {}
echo Product::getClassName(); // "Product"
echo User::getClassName(); // "User"
I think it's important to mention little difference between 'self' and 'static' in PHP as 'best answer' uses 'static' which can give confusing result to some people.
<?php
class X {
function getStatic() {
// gets THIS class of instance of object
// that extends class in which is definied function
return static::class;
}
function getSelf() {
// gets THIS class of class in which function is declared
return self::class;
}
}
class Y extends X {
}
class Z extends Y {
}
$x = new X();
$y = new Y();
$z = new Z();
echo 'X:' . $x->getStatic() . ', ' . $x->getSelf() .
', Y: ' . $y->getStatic() . ', ' . $y->getSelf() .
', Z: ' . $z->getStatic() . ', ' . $z->getSelf();
Results:
X: X, X
Y: Y, X
Z: Z, X
This will return pure class name even when using namespace:
echo substr(strrchr(__CLASS__, "\\"), 1);
end(preg_split("#(\\\\|\\/)#", Class_Name::class))
Class_Name::class: return the class with the namespace. So after you only need to create an array, then get the last value of the array.
From PHP 8.0, you can use ::class even on objects:
$object = new \SplPriorityQueue();
assert($object::class === \SplPriorityQueue::class);
<?php
namespace CMS;
class Model {
const _class = __CLASS__;
}
echo Model::_class; // will return 'CMS\Model'
for older than PHP 5.5

Reflection class PHP from file?

I wanna get value from Class PHP without initialize this Class.
For this I give the file path where this class, for it to be reviewed, but not initialized.
My Idea:
<?php
$reflection = new ReflectionClass( '/var/www/classes/Base.php' );
$version = $reflection->getProperty('version')->getValue( );
if( $version >= 1 )
{
return true;
}
return false;
?>
BASE.PHP
<?php
class Base
{
private $version = 2;
}
?>
whats about static? its much simpler:
<?php
class Base
{
public static $version = 2; // or $version = array(1,2,3);
}
if(is_array(Base::$version)) {
print_r(Base::$version);
} else {
echo Base::$version;
}
?>
How about a protected variable with a getter.
class Base {
protected $version = array(2,3,4,5,6);
public function __version() { return $this->version; }
}
You can instantiate this anywhere you like, or extend it to add functions to it. The version will be constant across any extensions, so bear that in mind.
Usage is as simple as $yourClass->__version(). Named it similar to a magic method's name in order to prevent function name collision. It can be redefined by extensions if needed.

How do I get class name in PHP?

public class MyClass {
}
In Java, we can get class name with String className = MyClass.class.getSimpleName();
How to do this in PHP? I already know get_class(), but it works only for objects. Currently I work in Active Record. I need statement like MyClass::className.
Since PHP 5.5 you can use class name resolution via ClassName::class.
See new features of PHP5.5.
<?php
namespace Name\Space;
class ClassName {}
echo ClassName::class;
?>
If you want to use this feature in your class method use static::class:
<?php
namespace Name\Space;
class ClassName {
/**
* #return string
*/
public function getNameOfClass()
{
return static::class;
}
}
$obj = new ClassName();
echo $obj->getNameOfClass();
?>
For older versions of PHP, you can use get_class().
You can use __CLASS__ within a class to get the name.
http://php.net/manual/en/language.constants.predefined.php
It sounds like you answered your own question. get_class will get you the class name. It is procedural and maybe that is what is causing the confusion. Take a look at the php documentation for get_class
Here is their example:
<?php
class foo
{
function name()
{
echo "My name is " , get_class($this) , "\n";
}
}
// create an object
$bar = new foo();
// external call
echo "Its name is " , get_class($bar) , "\n"; // It's name is foo
// internal call
$bar->name(); // My name is foo
To make it more like your example you could do something like:
<?php
class MyClass
{
public static function getClass()
{
return get_class();
}
}
Now you can do:
$className = MyClass::getClass();
This is somewhat limited, however, because if my class is extended it will still return 'MyClass'. We can use get_called_class instead, which relies on Late Static Binding, a relatively new feature, and requires PHP >= 5.3.
<?php
class MyClass
{
public static function getClass()
{
return get_called_class();
}
public static function getDefiningClass()
{
return get_class();
}
}
class MyExtendedClass extends MyClass {}
$className = MyClass::getClass(); // 'MyClass'
$className = MyExtendedClass::getClass(); // 'MyExtendedClass'
$className = MyExtendedClass::getDefiningClass(); // 'MyClass'
It looks like ReflectionClass is a pretty productive option.
class MyClass {
public function test() {
// 'MyClass'
return (new \ReflectionClass($this))->getShortName();
}
}
Benchmark:
Method Name Iterations Average Time Ops/second
-------------- ------------ -------------- -------------
testExplode : [10,000 ] [0.0000020221710] [494,518.01547]
testSubstring : [10,000 ] [0.0000017177343] [582,162.19968]
testReflection: [10,000 ] [0.0000015984058] [625,623.34059]
To get class name you can use ReflectionClass
class MyClass {
public function myNameIs(){
return (new \ReflectionClass($this))->getShortName();
}
}
Now, I have answer for my problem. Thanks to Brad for the link, I find the answer here. And thanks to J.Money for the idea. My solution:
<?php
class Model
{
public static function getClassName() {
return get_called_class();
}
}
class Product extends Model {}
class User extends Model {}
echo Product::getClassName(); // "Product"
echo User::getClassName(); // "User"
I think it's important to mention little difference between 'self' and 'static' in PHP as 'best answer' uses 'static' which can give confusing result to some people.
<?php
class X {
function getStatic() {
// gets THIS class of instance of object
// that extends class in which is definied function
return static::class;
}
function getSelf() {
// gets THIS class of class in which function is declared
return self::class;
}
}
class Y extends X {
}
class Z extends Y {
}
$x = new X();
$y = new Y();
$z = new Z();
echo 'X:' . $x->getStatic() . ', ' . $x->getSelf() .
', Y: ' . $y->getStatic() . ', ' . $y->getSelf() .
', Z: ' . $z->getStatic() . ', ' . $z->getSelf();
Results:
X: X, X
Y: Y, X
Z: Z, X
This will return pure class name even when using namespace:
echo substr(strrchr(__CLASS__, "\\"), 1);
end(preg_split("#(\\\\|\\/)#", Class_Name::class))
Class_Name::class: return the class with the namespace. So after you only need to create an array, then get the last value of the array.
From PHP 8.0, you can use ::class even on objects:
$object = new \SplPriorityQueue();
assert($object::class === \SplPriorityQueue::class);
<?php
namespace CMS;
class Model {
const _class = __CLASS__;
}
echo Model::_class; // will return 'CMS\Model'
for older than PHP 5.5

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

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