I've got this:
use XXX\Driver\Driver;
...
var_dump(class_exists('Driver')); // false
$driver = new Driver(); // prints 123123123 since I put an echo in the constructor of this class
exit;
Well... this behaviour is quite irrational (creating objects of classes that according to PHP do not exist). Is there any way to check if a class exist under given namespace?
In order to check class you must specify it with namespace, full path:
namespace Foo;
class Bar
{
}
and
var_dump(class_exists('Bar'), class_exists('\Foo\Bar')); //false, true
-i.e. you must specify full path to class. You defined it in your namespace and not in global context.
However, if you do import the class within the namespace like you do in your sample, you can reference it via imported name and without namespace, but that does not allow you to do that within dynamic constructions and in particular, in-line strings that forms class name. For example, all following will fail:
namespace Foo;
class Bar {
public static function baz() {}
}
use Foo\Bar;
var_dump(class_exists('Bar')); //false
var_dump(method_exists('Bar', 'baz')); //false
$ref = "Bar";
$obj = new $ref(); //fatal
and so on. The issue lies within the mechanics of working for imported aliases. So when working with such constructions, you have to specify full path:
var_dump(class_exists('\Foo\Bar')); //true
var_dump(method_exists('\Foo\Bar', 'baz')); //true
$ref = 'Foo\Bar';
$obj = new $ref(); //ok
The issue (as mentioned in the class_exists() manual page user notes) is that aliases aren't taken into account whenever a class name is given as a string. This also affects other functions that take a class name, such as is_a(). Consequently, if you give the class name in a string, you must include the full namespace (e.g. '\XXX\Driver\Driver', 'XXX\\Driver\\Driver').
PHP 5.5 introduced the class constant for just this purpose:
use XXX\Driver\Driver;
...
if (class_exists(Driver::class)) {
...
}
Related
I have something like this:
use Just\Some\Namespace\ClassName;
class A
{
public static function doStuff()
{
echo function_name(ClassName);
}
}
A::doStuff();
What I want to see printed is this: Just\Some\Namespace\ClassName
The whole idea is that I want to get the full namespace of a class without creating an object of that class.
I searched online and the closest I've came to a solution is the answers in this question. However, none of them is what I need.
I could use reflection like this:
$reflectionClass = new ReflectionClass('Just\\Some\\Namespace\\ClassName');
echo $reflectionClass->getNamespaceName();
but I would have to specify the namespace in order to create ReflectionClass. If I already know it, I wouldn't need it anymore.
What can I replace function_name with in my code in order to get Just\Some\Namespace\ClassName ?
As of PHP5.5 you can use the class keyword for namespace resolution
Since PHP 5.5, the class keyword is also used for class name
resolution. You can get a string containing the fully qualified name
of the ClassName class by using ClassName::class. This is particularly
useful with namespaced classes
use Magic constants __CLASS__
The class name. (Added in PHP 4.3.0) As of PHP 5 this constant returns
the class name as it was declared (case-sensitive). In PHP 4 its value
is always lowercased. The class name includes the namespace it was
declared in (e.g. Foo\Bar). Note that as of PHP 5.4 CLASS works
also in traits. When used in a trait method, CLASS is the name of
the class the trait is used in
like
echo __CLASS__; // The class name.
Try this
use Just\Some\Namespace\ClassName;
class A
{
public static function doStuff()
{
echo function_name(ClassName::class);
}
}
A::doStuff();
Best way to get the class "basename" (without namespace) of the given object / class.
function class_basename($class)
{
$class = is_object($class) ? get_class($class) : $class;
return basename(str_replace('\\', '/', $class));
}
If you try using class_exists() inside a method of a class in PHP you have to specify the full name of the class--the current namespace is not respected. For example if my class is:
<?
namespace Foo;
class Bar{
public function doesBooClassExist(){
return class_exists('Boo');
}
}
And Boo is a class (which properly autoloads) and looks like this
namespace Foo;
class Boo{
// stuff in here
}
if I try:
$bar = new Bar();
$success = $bar->doesBooClassExist();
var_dump($success);
you'll get a false... is there an alternative way to do this without having to explicitly specify the full class name ( i.e. class_exits('Foo\Boo') )?
Prior to 5.5, the best way to do this is to always use the fully qualified class name:
public function doesBooClassExist() {
return class_exists('Foo\Boo');
}
It's not difficult, and it makes it absolutely clear what you're referring to. Remember, you should be going for readability. Namespace imports are handy for writing, but make reading confusing (because you need to keep in mind the current namespace and any imports when reading code).
However, in 5.5, there's a new construct coming:
public function doesBooClassExist() {
return class_exists(Boo::class);
}
The class pseudo magic constant can be put onto any identifier and it will return the fully qualified class name that it will resolve to.......
Example in file 1:
namespace A;
class Foo{
}
file 2:
use A\Foo;
do_stuff('A\Foo'); // <- need namespace here :(
Foo::someStaticMethod(); // <- namespace not required :D
Is there any way I can pass class names in function arguments like constants or something, so I don't need to prepend the namespace?
Update :)
When I know, that I need to pass the classnames of some classes around as string I'm used to create special class constant
namespace Foo\Bar;
class A {
const __NAMESPACE = __NAMESPACE__;
const __CLASS = __CLASS__;
}
Now you can reference the classname like
use Foo\Bar\A as Baz;
echo Baz::__CLASS;
With PHP5.5 this will be builtin
echo Baz::class;
Full-Qualified-Names (FQN) for namespaces always starts with a namespace separator
do_stuff('\A\Foo');
except (and thats the only exception) in use-statements, because there can only appear complete namespace identifiers, so for convenience you can omit it there.
However, a string is a string and where you use it as a class name is out of scope of the interpreter, so it lost the reference to the former use A\Foo-aliasing. With PHP5.5 you can write Foo::class, but I think thats not an option right now ;)
You could instantiate a new object, then call get_class() to get the fully qualified name for the class.
use A\Foo;
$foo = new Foo();
do_stuff(get_class($foo)); // get_class($foo) = '\A\Foo'
This means that the namespace of Foo is only defined by the use statement (ie. less code maintenance).
Or you can pass class reflection.
ReflectionClass
No, not without tracing the caller, as far as I know. The function you are calling must exists within the same namespace as the object you are trying to pass.
You might want to have a look at the debug_backtrace function if you require the namespace resolution. But this requires the file-paths to be translated into namespace resolutions or similar.
This is however possible: (I see Andrew has answered with the same type of solution.)
function doStuff ($obj)
{
$name = (is_object($obj))
? (new ReflectionClass(get_class($obj)))->getName()
: $obj;
// $name will now contain the fully qualified name
}
namespace Common;
class Test
{}
$testObj = new Test();
// This will work, but requires argument to be
// fully quialified or an instance of the object.
\doStuff($testObj);
\doStuff("\Common\Test");
I'll try to describe the situation I'm having problems with:
I have one main folder. I keep all files in there (empty classes for a reason), and one sub-folder, containing the same files, with all implementations here (empty classes extend them).
the main folder's namespace is declared as Project/Folder, and the sub-folder as Project/Folder/Subfolder. These are class's declarations:
namespace Project\Folder;
class Foo extends Subfolder\Foo { }
namespace Project\Folder\Subfolder;
class Foo { }
What I want to achieve is to be able to call other classes from inside of the Project\Folder\Subfolder\Foo through these empty classes on the lower level, with only its name, e.g.:
namespace Project\Folder\Subfolder;
class Foo {
function bar() {
Another_Class::do_something();
}
}
By default, there will be called Another_Class from the Project\Folder\Subfolder namespace. I want this to refer to Another_Class from the Project\Folder namespace with the same syntax - is that possible?
I hope I explained this clear enough, if not, write a commend, and I'll try to make it clearer.
You can achieve that using the use statement.
use Project\Folder\Subfolder\Another_Class as SomeAlias;
// ...
SomeAlias::doSomething();
// or
$object = new SomeAlias();
$object->doSomething();
Alternatively, you would have to reference the entire namespace:
\Project\Folder\Subfolder\Another_Class::doSomething();
// or
$object = new \Project\Folder\Subfolder\Another_Class();
$object->doSomething();
More information here.
If I have a class structure with a value that can either be true or false, that doesn't change, currently implemented as variables would it be better to change them to constants, such as:
class Parent {
const BOOL_CONST = false;
...
}
class SomeChild extends Parent {
const BOOL_CONST = true;
...
}
Later I have an object which may be of any type in that class hierarchy, either the parent or one of its children, and some of the children may, like 'SomeChild' have overloaded the value to be true.
Is there some way I can access the constant without knowing the class? In other words can I do something like:
$object->BOOL_CONST
Or would it be better to leave these values as variables, even though they really shouldn't change?
UPDATE
I've reworded my question above to better express what I was attempting to ask.
Is there some way I can access the constant without knowing the class?
In other words can I do something like:
Yes, in order to reference a constant, you will want to use the following constructs:
self::NAME_OF_CONSTANT: give me a constant defined in this class; if I don't define it, get it from my parent
static::NAME_OF_CONSTANT: give me a constant defined in this class ONLY; never look to my parent for it
parent::NAME_OF_CONSTANT: give me a constant defined in my parent class ONLY; never look to myself for it
BTW, you used the term "overloaded"; however, I believe you meant to say "overridden". Overloading has a different semantic meaning in object oriented languages.
Constant as access with the double colon ::
Parent::BOOL_CONST
SomeChild::BOOL_CONST
within the class
parent::BOOL_CONST
self::BOOL_CONST
PHP 5.3 now accepts the object as the class reference: $this::BOOL_CONST is now accepted.
//
// http://php.net/manual/en/language.oop5.constants.php
//
// As of PHP 5.3.0, it's possible to
// reference the class using a variable.
// The variable's value can not be a keyword
// (e.g. self, parent and static).
//
// I renamed "Parent" class name to "constantes"
// because the classname "Parent" can be confused with "parent::" scope
class constantes
{
const test = false;
}
// I renamed "SomeChild" too, with no reason...
class OverloadConst extends constantes
{
const test = true;
public function waysToGetTheConstant()
{
var_dump(array('$this'=>$this::test)); // true, also usable outside the class
var_dump(array('self::'=>self::test)); // true, only usable inside the class
var_dump(array('parent::'=>parent::test)); // false, only usable inside the class
var_dump(array('static::'=>static::test)); // true, should be in class's static methods, see http://php.net/manual/en/language.oop5.late-static-bindings.php
}
}
// Classic way: use the class name
var_dump(array('Using classname' => OverloadConst::test));
// PHP 5.3 way: use the object
$object = new OverloadConst();
var_dump(array('Using object' => $object::test));
$object->waysToGetTheConstant();
Note that you can override a class constant, but not an interface constant.
If constantes is an interface that OverloadConsts implements, then you can not override its const test (or BOOL_CONST).
Sources
Constants in PHP 5.3: http://php.net/manual/en/language.oop5.constants.php
Late static Binding: http://php.net/manual/en/language.oop5.late-static-bindings.php
No, you can't access constants from an object context, but you could use reflection to grab the class of $object and then use :: to get BOOL_CONST. So:
$class = get_class($object);
$class::BOOL_CONST;
Okay, no, that's not technically reflection. Also, I'm not 100% sure if $class:: will resolve correctly. Use the actual ReflectionClass classes if the above doesn't work.
You cannot do $object->BOOL_CONST, since class constants have to be called statically (SomeChild::BOOLCONSTANT).
However, maybe you can try something like that: // edit: this works :)
$class = get_class($object);
$const = $class::BOOL_CONST;