Essentially, I seek to pass a static class method to a callback, but do not wish to do so using a hard-coded string, but rather the fully-qualified class method literal. We can do that using classes like so:
$name = NS\FooClass::class;
instead of:
$name = 'NS\FooClass';
which will give us the string of the fully-qualified name of the class. I seek to be able to do something similar for a class method like so:
$name = NS\FooClass::foo_method::method;
instead of:
$name = 'NS\FooClass::foo_method';
It is more manageable and I can use the IDE functionality way better using the literals. Any similar way I can achieve what I want with the class methods without using strings?
There is currently no such mechanism built into the language. It has been suggested - see for instance this discussion from Feb 2020 - but there are more nuances to think about than might be immediately apparent; notably:
Should the syntax resolve at run-time and check the existence of the class and the method (::class in most cases doesn't; a bare function like strlen::func would have to because of the way namespaces resolve; an object implementing __callStatic could never be used this way)?
Should the result be a string, an array (see below), or a Closure object?
Anyway, that's a topic for elsewhere...
As the manual page on the callable type says, there are two ways to specify a static method for use as a callback:
As a string, as in your example 'NS\FooClass::foo_method'
As an array where the first part is a class name, and the second part is a the method name: ['NS\FooClass', 'foo_method']
Since only the class name needs to be qualified with namespace information, you can use ::class with the second syntax to get nearly what you wanted:
$callback = [NS\FooClass::class, 'foo_method'];
This allows any decent IDE to spot the reference to the class, and allows you to reference it by an imported or aliased name.
It's worth noting that if the callable type is specified in a parameter or return type declaration or a docblock, some IDEs (e.g. PhpStorm) will "understand" either format as a reference to the method, and include it in features like "find usages" and "go to declaration".
Related
What I wanted to is to create class instance from string in Symfony framework to use Doctrine Repository. Here is my code block:
$type = "SomeClassName";
$productType = $this->productTypeProvider->getProductType($product->getBarcode());
$repository = $this->doctrine->getRepository($type::class);
I want to use SomeClassName::class above but it gives me the following error. Any help would be appeciated:
Cannot use "::class" on value of type string (500 Internal Server Error)
As mentioned above, using the full class name worked perfectly.
To understand why this doesn't work, it's useful to have some background on how namespaces are implemented in PHP:
Every class, interface, trait, and enum has a "fully-qualified class name" (FQCN), which can contain zero or more namespace separators (\)
A particular file, or code-block, can have a "current namespace" defined.
Within that block, you can have any number of use statements, which provide short-hands within the scope of that file.
The expansion of the current namespace, and the currently in-scope use statements, happens at compile-time - in case you're not aware, PHP is a compiled language, the compiler is just invoked automatically, for a single file.
The ::class syntax is part of this compile-time expansion: it substitutes any relevant use statements or current namespace, and then treats the result as a string. In other words, this:
namespace Foo\Bar;
use Something\Else;
use Something\Else\Again as Alias;
echo Example::class;
echo Else::class;
echo Else\More::class;
echo Alias::class;
Is compiled exactly as though it said this:
echo 'Foo\Bar\Example';
echo 'Something\Else';
echo 'Something\Else\More';
echo 'Something\Else\Again';
Those strings can then be passed around to wherever needs an FQCN.
The key point here is that this all happens before any code in the file is executed. When you tried to write $type::class, the value of $type is something that will only be known at run-time; far too late for the namespace expansion in the compiler to affect it. Theoretically, the compiler could know what you meant by "SomeClassName"::class, but it would be unnecessary extra complexity since you can just write SomeClassName::class instead.
If you are building a string at run-time, you are responsible for turning it into an FQCN.
Note that despite its name, ::class doesn't actually check that the result is a class name, it just does string manipulation based on the current namespace and use statements, so the following are all equivalent:
namespace Example;
use App\Entity; // [1]
use App\Entity\SomeClassName; // [2]
// explicit FQCN
$type = 'App\Entity\SomeClassName';
// straight-forward expansion of [2]
$type = SomeClassName::class;
// expansion of [1]
$type = Entity\SomeClassName::class;
// expand the namespace via [1] then add class name
$namespace = Entity::class;
$type = $namespace . '\SomeClassName';
// expand to a non-existent class, then add more text
$prefix = Entity\So::class;
$type = $prefix . 'meClassName';
In a CakePHP Plugin documentation there is the following code line: $validator->provider('upload', \Josegonzalez\Upload\Validation\DefaultValidation::class);
\Josegonzalez\Upload\Validation\DefaultValidation is the namespace, but I didn't understand the ::class. Could someone explain it? I didn't find anything in PHP documentation.
the class constant simply returns the full name of the class (with namespace) as a string. So instead of passing as string to some method that requires it, you pass it the PHP way. It just looks nice, for example:
$validator->provider('upload',\Josegonzalez\Upload\Validation\DefaultValidation::class);
AND
$validator->provider('upload', '\Josegonzalez\Upload\Validation\DefaultValidation');
Both Are Same
And another advantage of this is that, if you need full class name several times in a single file.. say onto multiple method calls as a parameter. You can simply use it on the top & then only the classname will return the full name with namespace. like this:
use \Josegonzalez\Upload\Validation\DefaultValidation;
$validator->provider('upload', DefaultValidation::class);
//you can use it on other places as well, if required.
$someOtherClass->someOtherMethod(DefaultValidation::class);
So, in short, it reduces the number of characters you need to type, and makes your code look cleaner.
The double colon (::) is the Scope Resolution Operator.
This is used to access protected methods and properties (and constants).
However, I have seen the use of ::class to return the string representation of the class. Like so:
namespace App;
class MyClass
{
}
var_dump(MyClass::class); # string(11) "App\MyClass"
I have searched far and wide for a documentation over this behaviour.
Is the above code also using the Scope Resolution Operator or is this something completely different? If so, what is the name of it?
If it is using the SRO, does that mean that PHP places a static property "class" behind the scenes? (I have been unable to override this static property though)
This is a new feature in PHP5.5, see: http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class
It's basically for getting the full class name. It's also not a static variable (see no preceding $) but a language feature using the class keyword. As per the note on php.net:
The class name resolution using ::class is a compile time transformation. That means at the time the class name string is created no autoloading has happened yet. As a consequence, class names are expanded even if the class does not exist. No error is issued in that case.
The operator ::class was introduced with PHP 5.5 and returns a string with the fully qualified class name.
In PHP, call_user_func(array(self, 'method_name')) doesn't work. The self keyword cannot be used in that context. I need to actually include the name of the class call_user_func(array('class_name', 'method_name')).
However, if I'm not in a static function, the $this variable does work in that context. Why the difference?
If you want the name of the current class context, use get_class() (without any parameters) or __CLASS__.
You've already written the difference; self is a keyword, and is not usable as a reference in an array (what kind of type should that be in PHP?). get_class() returns a string, and the array()-callback supports using a string as the first name to do a static call.
You can try with __CLASS__ to get the class name. Also it may work to use call_user_func('self::method_name') directly, but I didn't test it and the documentation about callbacks doesn't say anything about this.
self is just an undefined constant, so it expresses 'self'. So these two are the same:
array(self, 'method_name');
array('self', 'method_name');
And depending on the PHP version you use, this actually works, see Demo.
In case it does not work with your PHP version, some alternatives are:
call_user_func(array(__CLASS__, 'method_name'));
or
call_user_func(__CLASS__.'::method_name'));
or (in case you don't need call_user_func):
self::method_name();
Since PHP 5.5, you can do [self::class, 'methodName'].
::class is really useful for situations where you have a class name (maybe a local alias) and you need to generate the full class name as a string.
In PHP 5.3, you can write call_user_func('self::method') or call_user_func(array('self', 'method')). I suppose the latter could work in older versions as well.
In C#, variables and other things can be named protected names such as "class" by prepending the name with an # sign. So, #class is a valid name. Is it possible to do this same thing in PHP? I am using a class of constants to simulate an enum for HTML attributes such as ID, and Class. For now I am using "CssClass" but I'd rather use the name Class somehow.
Nope, not possible, at least not for class constants.
You cannot use any of the following [reserved] words as constants, class names, function or method names.
I don't know about C#, but there isn't any special symbol in PHP to transform a keyword into an identifier. As long as you don't name it exactly the same as a keyword (barring letter case), it'll just be any normal constant name.
How about a (different since it's not just CSS) prefix? Gets repetitive to type, but is a nice workaround. I realize this may be redundant as well if your class is named something like HTMLAttribute, but it's the easiest way out.
const A_ID = 'id';
const A_CLASS = 'class';
// etc
Yes, it is possible.
In fact you can define anything as constant:
define("define", 1);
define("class", 1);
define("if", 1);
define("=.+*", 1);
However, you can not use all defined constants.
You can query them with constant("if") again. But this is not exactly what you asked for. So unlike C# there is no shortcut to use any random constant. But as for naming them, there are almost no restrictions. (Might be a bug though. It's PHP.)
Constants:
The name of a constant follows the same rules as any label in PHP. A valid constant name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
List of reserved keywords:
These words have special meaning in PHP. Some of them represent things which look like functions, some look like constants, and so on--but they're not, really: they are language constructs. You cannot use any of the following words as constants, class names, function or method name.
[see list here]
Within these rules you're free to make up your names. So, for instance, you could name a constant _CLASS, but not CLASS. I'd avoid the use of such ambiguous names though and namespace constants that are particular to the app, like MYAPP_CLASS.
Going from PHP5 to PHP7, a class constant could be named almost anything:
class ReservedWord
{
// Works in PHP >= 7.0 only
const NULL = null;
const TRUE = true;
}
However, thanks to this part of the manual and this comment, I've found that a class constant cannot be named these few things (see the test here):
class
static
__halt_compiler (oh, that was so useful!)
Edit: As I found in here in an RFC, the reason why class constant does not work is the name resolution ::class. However, still no idea about the two others.