Hey List,
What do you think about adding a typeof() operator to PHP?
It could go something like this:
class User
{
public $name;
}
/**
* @var ReflectionClass $user_type
* @var ReflectionProperty $user_name_property
*/
$user_type = typeof(User);
$user_name_property = typeof(User::$name);
Mind you, this is an operator and not a function - User and User::$name are
interpreted as symbols.
This would allow for static analysis of class and property-references.
Why? Because you can't use static analysis on code like this:
$user_type = new ReflectionClass('User');
$user_name_property = new ReflectionProperty('User', 'name');
'User' and 'name' are just strings, and strings are dead matter when it
comes to static analysis.
Most form builders and ORMs (and other reflective meta-programming
components) require references to properties or classes, e.g.:
$form->textInputFor($user, 'name');
There is no knowledge that 'name' actually refers to the symbol User::$name
and hence to way to perform static analysis or use refactoring tools on
this code.
Compare to:
$form->textInputFor($user, typeof(User::$name));
This code can be analyzed and refactored, because the reference to the
User::$name property is now literal.
Actually having to type out typeof() may not be the most elegant approach,
and using this syntax it does resemble a function - having a more distinct
syntax might be better.
But those things aside, what do you think about having a way to statically
reference types and members?
Thanks,
Rasmus Schultz
> Hey List,
>
> What do you think about adding a typeof() operator to PHP?
>
> It could go something like this:
>
> class User
> {
> public $name;
> }
>
> /**
> * @var ReflectionClass $user_type
> * @var ReflectionProperty $user_name_property
> */
>
> $user_type = typeof(User);
>
> $user_name_property = typeof(User::$name);
>
> Mind you, this is an operator and not a function - User and User::$name are
> interpreted as symbols.
>
> This would allow for static analysis of class and property-references.
>
> Why? Because you can't use static analysis on code like this:
>
> $user_type = new ReflectionClass('User');
>
> $user_name_property = new ReflectionProperty('User', 'name');
>
> 'User' and 'name' are just strings, and strings are dead matter when it
> comes to static analysis.
>
It is certainly possible for static analyses to reason about literal
strings. Handling reflective code is not that hard when its arguments are
literals.
>
> Most form builders and ORMs (and other reflective meta-programming
> components) require references to properties or classes, e.g.:
>
> $form->textInputFor($user, 'name');
>
> There is no knowledge that 'name' actually refers to the symbol User::$name
> and hence to way to perform static analysis or use refactoring tools on
> this code.
>
> Compare to:
>
> $form->textInputFor($user, typeof(User::$name));
>
To me this code makes no sense. I expect typeof to return a type, since we
have no first-class types in PHP, I expect it to return a string
representing the type.
If I understand your example, here it returns a Reflection* ?
What about typeof($a->b) or even typeof($a->$foo) ? I assume both of them
would be forbidden ?
> This code can be analyzed and refactored, because the reference to the
> User::$name property is now literal.
>
>
Difficulty to statically analyze code comes from its dynamic nature, to me
typeOf(User::$name) is no less dynamic than new ReflectionProperty("User",
"name").
> Actually having to type out typeof() may not be the most elegant approach,
> and using this syntax it does resemble a function - having a more distinct
> syntax might be better.
>
> But those things aside, what do you think about having a way to statically
> reference types and members?
> Thanks,
> Rasmus Schultz
>
--
Etienne Kneuss
Thank you for taking the time to discuss this.
> It is certainly possible for static analyses to reason about literal
strings.
It is possible to make a "qualified guess", but there are no guarantees -
for example, with automated refactoring in PhpStorm, even when enabling the
search for literal strings, there are no guarantees, as the static analyzer
can make no distinction between the string 'name' and the
property-reference User::$name ... to highlight one problem with this
approach, how many model-classes have a property called $name ?
> To me this code makes no sense. I expect typeof to return a type, since
we have no first-class types in PHP, I expect it to return a string
representing the type.
Okay, that would work fine, too - you would then be able to say:
$name_property = new ReflectionProperty(typeof(User::$name));
You're probably right, there is no need for a type-resolution operator to
return a class, could just be a string.
I'm not sure how useful a property-reference would be as a string, however
- let me get back to that issue at the end of this e-mail.
> What about typeof($a->b) or even typeof($a->$foo) ? I assume both of them
would be forbidden ?
Not so much "forbidden", merely "unnecessary" - I'm not trying to invent
another way to refer to classes or members dynamically, we can do that
already, but rather to provide a way to refer to them statically.
> Difficulty to statically analyze code comes from its dynamic nature, to
me typeOf(User::$name) is no less dynamic than new
ReflectionProperty("User", "name").
Whether it's *evaluated* dynamically is of no concern - the point is, you
would have a syntax for statically referencing a class or property.
Ralph Schindler's recently implemented ClassName::class syntax solves this
problem for classes - you can now statically reference a class-name, which
means that you will (soon) be able to safely rename classes.
Unfortunately, the need to reference a property is even more common that
referencing a class, in my experience - you typically reference the class
once, and then repeatedly reference properties many times for many reasons;
form-helpers, etc.
On the issue of whether this operator would returns strings or something
else, perhaps it ought to return objects. If typeof(User::$name) evaluates
as "User::$name", you would need to use `explode()` or something to break it
apart afterwards, which would be inconvenient. Another quarrel I have with
ClassName::class, is that it does just return a string, rather than
returning a type that indicates what it is - a class reference.
Having multiple return-types for this operator would probably be a better
approach. Let me demonstrate why:
class FormHelper
{
...
public function textInput($object, PropertyReference $prop)
{
$value = $prop->getValue($object);
$property = new ReflectionProperty($prop);
...
}
}
$form = new FormHelper();
echo $form->textInput($object, typeof(User::$name));
By introducing lightweight member-reference types (e.g. ClassReference,
PropertyReference and MethodReference) we get something much more robust to
pass around and work with than just strings - it enables us to use these
classes as type-arguments. In the example above, what we want passed to the
textInput() method after all is *not* actually a string, but a reference to
a property.
Due to the dynamic nature of PHP, we can (and probably should) let these go
unchecked. That is, at run-time, typeof(User::$name) would return a
PropertyReference with properties $class = 'app\model\User' and $name =
'name', but would not invoke auto-loading or guarantee that the reference
is valid.
Static analysis, on the other hand, would reveal whether you're making
valid class/member-references, and automated refactoring would become
simpler and much safer.
The constructors would be public, so you would be able to create a
PropertyReference dynamically, e.g. new PropertyReference($className,
$propertyName) if necessary.
Going from reflection-objects to references and back, could be made
extremely simple, too - e.g.:
$user_class = typeof(User)->class; // => 'User' (string)
$user_reflection = new ClassReflection($user_class);
$user_ref = $user_reflection->reference; // => ClassReference
$user_reflection_again = $user_ref->reflection; // => ReflectionClass
Same goes for property-references and ReflectionProperty, and
method-references and ReflectionMethod.
I could see this being really useful and powerful in view-engines,
object/relational-mappers, data-mappers, validation-frameworks, etc.
Nobody else thinks this is interesting?
- Rasmus Schultz
> Dear Rasmus,
>
>
> On Thu, Mar 14, 2013 at 5:26 PM, Rasmus Schultz
>
>> Hey List,
>>
>> What do you think about adding a typeof() operator to PHP?
>>
>> It could go something like this:
>>
>> class User
>> {
>> public $name;
>> }
>>
>> /**
>> * @var ReflectionClass $user_type
>> * @var ReflectionProperty $user_name_property
>> */
>>
>> $user_type = typeof(User);
>>
>> $user_name_property = typeof(User::$name);
>>
>> Mind you, this is an operator and not a function - User and User::$name
>> are
>> interpreted as symbols.
>>
>> This would allow for static analysis of class and property-references.
>>
>> Why? Because you can't use static analysis on code like this:
>>
>> $user_type = new ReflectionClass('User');
>>
>> $user_name_property = new ReflectionProperty('User', 'name');
>>
>> 'User' and 'name' are just strings, and strings are dead matter when it
>> comes to static analysis.
>>
>
> It is certainly possible for static analyses to reason about literal
> strings. Handling reflective code is not that hard when its arguments are
> literals.
>
>
>>
>> Most form builders and ORMs (and other reflective meta-programming
>> components) require references to properties or classes, e.g.:
>>
>> $form->textInputFor($user, 'name');
>>
>> There is no knowledge that 'name' actually refers to the symbol
>> User::$name
>> and hence to way to perform static analysis or use refactoring tools on
>> this code.
>>
>> Compare to:
>>
>> $form->textInputFor($user, typeof(User::$name));
>>
>
>
> To me this code makes no sense. I expect typeof to return a type, since we
> have no first-class types in PHP, I expect it to return a string
> representing the type.
>
> If I understand your example, here it returns a Reflection* ?
>
> What about typeof($a->b) or even typeof($a->$foo) ? I assume both of them
> would be forbidden ?
>
>
>
>> This code can be analyzed and refactored, because the reference to the
>> User::$name property is now literal.
>>
>>
> Difficulty to statically analyze code comes from its dynamic nature, to me
> typeOf(User::$name) is no less dynamic than new ReflectionProperty("User",
> "name").
>
>
>> Actually having to type out typeof() may not be the most elegant approach,
>> and using this syntax it does resemble a function - having a more distinct
>> syntax might be better.
>>
>> But those things aside, what do you think about having a way to statically
>> reference types and members?
>
>
>> Thanks,
>> Rasmus Schultz
>>
>
>
>
> --
> Etienne Kneuss
Hey List,
What do you think about adding a typeof() operator to PHP?
gettype()
, get_class()
.
If you wanted gettype()
to return a classname instead of "object", then you'd have to add
reserved words for all the native types. Otherwise:
class string {}
gettype(new string);
???
Steve Clay
$user_type = typeof(User);
I missed this. We'll soon have User::class. This may resolve to, e.g., 'Foo\User'.
Steve Clay
I missed this. We'll soon have User::class. This may resolve to, e.g.,
'Foo\User'.
Right, and since the literal/string version of the class is as "1st
class citizen" as it could possibly get, it makes it easier for tools
like Studio/Storm/phpunit/etc to do better static analysis of class names.
The immediate usage for an IDE would be clean refactoring of type names.
-ralph