
Typed Properties in PHP 7.4 - brendt_gd
https://stitcher.io/blog/typed-properties-in-php-74
======
mooreds
Tons and tons of the web runs on PHP. It is a super easy language to learn and
be effective in quickly. Especially if you are talking about open source
content management systems, PHP is dominant.

Anything that improves overall code quality of this language should be
welcomed with open arms. Types, especially if they are backward compatible (as
these are) will do so.

I'd be interested in what kind of automated or ide support will be available,
but I suppose that won't be known until this is released later this year.

~~~
beberlei
PHPStorm has excellent support for types and version 2019.2 released last week
includes typed properties already:
[https://www.jetbrains.com/phpstorm/whatsnew/](https://www.jetbrains.com/phpstorm/whatsnew/)

Other editors (VSCode for example) can hook into Psalm or Phans Language
Server to get type information from these static analyzers.

Typing support in PHP Ides is usually excellent.

~~~
gchamonlive
at 199 USD yearly phpstorm is prohibitively expensive for small teams and
independent devs

I don't know the state of the language server for newer php versions, but
intellisense for vscode is too taxing on the CPU for relatively large projects
(20k+ lines of code)

~~~
stephenr
~2-5 hours gross income per year is nothing for a good tool.

~~~
tasuki
It's a little rude to assume how much people are earning. I think you might be
surprised how little people earn in certain parts of the world.

------
brendt_gd
I think that the biggest win with this feature, is not that PHP will do more
runtime type checks, but that the syntax is valid.

The past years we've seen several static analysers become more and more
popular in the PHP community. Tools like phpstan
([https://github.com/phpstan/phpstan](https://github.com/phpstan/phpstan)) and
psalm by Vimeo
([https://github.com/vimeo/psalm](https://github.com/vimeo/psalm)).

Allowing this syntax is a great way to write cleaner code that can be
statically analysed, which is far more superior than the runtime type checker.

~~~
TazeTSchnitzel
Having them checked at runtime is still important (I say this as someone who
was involved in scalar types getting declarations finally), because it means
that the types can't end up inaccurate without being noticed, weak typing can
be supported with certainty (you always get the type you ask for), and as a
bonus the runtime can perform optimisations (elided type checks).

------
RutZap
I'm really looking forward to this. After 10 years of programming in PHP and
maintaining loads of legacy projects I've come to greatly appreciate explicit
definitions and strong typing. IMHO being explicit always increases
maintainability.

~~~
noir_lord
Agreed, better types for PHP on the backend and TypeScript on the front makes
life much more pleasant.

~~~
some_developer
Same here.

Using GraphQL in the PHP backend und frontend TS. The schema is exported to TS
and thus everything is fully types. A blessing.

------
agumonkey
I find the timing odd [0] that php since v5 ressembles more and more Java very
explicit type system, while Java is going backward to more implicit and
infered.

All in all in 20 years we'll all be writing ml ?

[0] technological waves diffuse differently across cultures

~~~
jillesvangurp
Type inference does not compromise on static typing. It simply makes it less
verbose. IMHO type inference has removed a lot of the arguments against
statically compiled languages. Which is why things like Typescript are
becoming popular. Even Python has type annotations now.

~~~
minxomat
Although some devs (and style guides) do tend to resist inference in favor of
verbosity (C#). Subjectively, I love inference (like in F# or Crystal). I'm
not sure if an objective winner exists for best strategy, or how to even
measure that.

~~~
Crinus
It is certainly subjective, but i am one of those developers who dislike
inference in languages where it has them and prefer explicit typing out types
- in fact i also go as far as to avoid type aliases (e.g. i prefer something
like "const std::vector<some_template<some_foo>>" to "a_foo_vector" typedef
for the former).

The reason is that i want to know "on site" what exactly is going on when
reading and debugging code, especially outside a "smart" IDE, like when
diffing changes via the command line, a web-based VCS frontend or some code
review tool - especially since these tend to not show the entire code, meaning
i need to switch back and forth. But even with an IDE that can show me the
inferred types, it is almost always (while i say "almost always" i really mean
"always" since i haven't seen any other, but there might be some IDE i haven't
tried) done through some popup under the mouse cursor, meaning it is transient
and slow.

For similar reasons i dislike features like operator overloading, placing non-
self-initializing logic in constructors (and the equivalent in destructors),
properties for anything that involves complex calculations (unless the
language uses special syntax for properties that make them stand out when
used) especially for getters, i prefer to use names that hint to what the code
does (e.g. i'd use something like "get_foo" only if it doesn't do any
calculations and give you a precomputed or cached "foo", otherwise i'd use an
"action" verb like "calc_foo" or "find_foo" or whatever to hint that there is
some calculation going on and isn't just a dumb getter), i avoid exceptions as
they create hidden exit points for functions (and if the IDE/editor allows, i
use a more visible - like bold red - font for "return" or the language
equivalent so that explicit exit points are obvious), etc.

Yeah, these make code more verbose and you need to type more, but IMO the
negatives are grossly overwhined against and while the positives way too
overlooked.

~~~
viraptor
> who dislike inference in languages where it has them and prefer explicit
> typing out types

So you prefer

    
    
        std::vector<some_template<some_foo>> foo = new std::vector<some_template<some_foo>>();
    

Rather than replace the first one with var / let / auto / whatever your
language does? Why? The type is already there.

~~~
Crinus
In terms of "liking" it, no, in that very specific case where the type is
repeated and very long, i do not like it and i think "auto" (or var or let or
whatever) looks better. However in the interest of consistency (and to a
lesser extent, avoiding 'slippery slopes') i'd rather type that than use auto.

And even then that this is only in the very specific case you showed, where
the type is too long, it is repeated and the constructor parameters - if any -
are visually dwarfed by the duplicated type.

In practice these aren't as common as other uses of types like loop iterators
for member/obtained collections (where the use of 'auto' makes it impossible
to know the collection type unless you check the collection declaration) or
local variables you assign to the result of some function (the return type of
which again you need to check the function declaration to see) and - at least
in C++ - you rarely need to do "very_long_foo* bar = new very_long_foo" as a
stack allocated "very_long_foo bar" is enough (so no need to double type the
type).

------
naranha
I'm still not so convinced by PHP's runtime type checking system. It adds a
little overhead, I guess that's not such a big problem, but it adds also new
classes of runtime exceptions that can only be caught using tests, or with a
good static analyzer. Especially if you now upgrade your application to 7.4
you can only do it with confidence when you have 100% test coverage.

If a static analyzer can detect these mistakes, why even add runtime checks
for it? Think TypeScript/Javascript, after the compilation is done, most type
checks are removed for performance reasons.

Or perhaps there is something I don't see...

~~~
beberlei
If you upgrade your application to 7.4, all the code doesn't have typed
properties.

Only when you then add them after the upgrade you should have tests for that
or a static analyzer that tells you the changes you make are correct.

~~~
naranha
Yes that's true - it's a backwards compatible change.

Still, static analysis is necessary when you upgrade dependencies and for your
own code. For example for seeing whether adding a type to a property breaks
code somewhere else. Discovering type errors at runtime is often too late.

Luckily there are good static analyzers for PHP.

------
wwweston
"But, before you pounce on this feature and start porting all your existing
model classes to use this feature, you should definitely read this."

[https://dev.to/mindplay/php-typed-properties-think-
twice-382...](https://dev.to/mindplay/php-typed-properties-think-twice-3824)

------
tasubotadas
Slowly PHP is converging to what Java has been like 15 years ago.

~~~
k__
True.

I'm also seeing that with JavaScript and TypeScript.

TS feels very C# like.

Luckily there seem to be counter movements with languages like Reason,
ClojureScript and PureScript.

~~~
oblio
We're basically getting a huge Algol mono-language.

C is not really moving, but C++ is also converging towards this Java++
language.

Java itself is finally moving.

C# is a bit ahead of the curve, because it can follow the trail blazed by F#.

PHP started moving towards Java back from when PHP5 was introduced.

Javascript is moving towards Typescript which is following C#'s lead.

Python is kind of the only non-Algol mainstream hold out.

Did I miss any of the big enterprise languages? :-?

~~~
plopz
How is Javascript moving towards Typescript?

~~~
k__
I don't know either.

TS was influenced mainly by C# at the start, then from FlowType and currently
it's mainly influenced by ECMAScript.

So to me it feels like the other way around.

------
namelosw
Imo optional typing like this is the best trade-off for now.

Usually, statically typed languages is not expressive. There are a lot of
abstractions are hard to express in statically typed languages. Even in
Haskell. With optional typing, you can express usual ideas with types, and
cheat with 'any' when not applicable.

Another thing is the language itself can possibly execute without compile or
even type check. Making developing feedback cycle much smoother when running
tests or just save and refresh.

~~~
6gvONxR4sf7o
What's the difference between static typing with an Any type and optional
typing?

~~~
jesseschalken
An "Any type" is optional typing if you don't need to downcast it in order to
use it, like the "any" type in TypeScript and Flow.

If the "Any" type is just a top type but you have to downcast it to use it,
like in Kotlin, then it's not considered optional typing, since you still have
to talk about types ;).

~~~
6gvONxR4sf7o
That's very clear and interesting. Thanks :)

------
donatj
I love the idea, but the implementation is IMHO poor. It doesn’t require non-
nullables to be defined by the end of the constructor. Instead it moreorless
adds a new type of NULL to the language (always a mistake) which causes
parameters to throw runtime errors on _call_ if they were not defined.

This adds up to essentially making everything nullable, regardless of if the
type is defined as nullable, and adding an entire class of runtime errors that
could have been avoided.

~~~
TazeTSchnitzel
The idea behind it was to avoid having every typed property be nullable. It's
important to note that the undefined state of a property cannot be read as a
value, and so can't be propagated anywhere.

~~~
donatj
Just do it like TypeScript though where they just have to be defined by _the
end of the constructor_ or be nullable/have a default.

A new state was not necessary.

~~~
TazeTSchnitzel
I think the problem is that doesn't work for cases where you have a minimal
constructor and do the rest in a static method which wraps it, or where you
construct the class while skipping the constructor (and probably some other
cases). PHP allows more flexibility here than other languages, and these sorts
of cases are not uncommon. Mocking libraries and databases interfaces do the
latter, and using the former to simulate multiple constructors is a common
pattern.

------
ppeetteerr
"Because uninitialized state is checked when accessing a property, you're able
to create an object with an uninitialized property, even though its type is
non-nullable."

This is going to result in a new series of run-time-only bugs that PHP is
famous for. I love the language but it's 2019 and we should be past this.
Also, where are the generics?

~~~
mevile
Ideally you'd get a compile error, but there is no compiler in PHP. Run time
is the only time PHP can tell you there's a problem.

------
felixfbecker
I'm pretty excited about the new "uninitialized" state. Back in my PHP days I
also had the phase of writing my own framework and ORM, and it annoyed me that
I couldn't distinguish between "not loaded" and database null. This seems like
it would make the "not loaded" state possible!

~~~
jesseschalken
Although isset() still returns false for null ;)

~~~
wwweston
Which is a little weird, but cases where you'd want `isset` to treat variables
intentionally set to null the same as variables that are entirely undeclared
are considerably more frequent (just ran into one yesterday!) than cases where
you'd need to carefully distinguish them.

~~~
jesseschalken
It's also great at hiding bugs where you mistyped the variable name passed to
isset(). ;)

------
kijin
> All types are allowed, except void and callable

Urgh. It baffles me that 10 years after their introduction, anonymous
functions (closures) remain useless when assigned to instance properties.

    
    
        class Foo {
            public $var;
        }
        $foo = new Foo();
        $foo->bar = function() { echo "Hello World\n"; };
        $foo->bar();
        Uncaught Error: Call to undefined method Foo::bar()
    

Callables work perfectly fine when assigned to standalone variables or array
members: e.g. $foo[0](); But they become nonfunctional when assigned to
instance properties. I understand that using closures as instance properties
would make them hard to distinguish from methods, but blurring that
distinction is just so damn useful in JavaScript!

~~~
adamwathan
You can invoke an anonymous function assigned to an instance property using
`__invoke()`:

    
    
        class Foo {
            public $var;
        }
        $foo = new Foo();
        $foo->bar = function() { echo "Hello World\n"; };
        $foo->bar->__invoke();
    

Not pretty but that's how I've always done it.

~~~
luckylion
Certainly prettier than using _call_user_func($foo- >bar)_, so that's
something.

~~~
rossriley
even better, you can surround the class and method reference in parentheses
and call normally.

Example:

    
    
        $c = new stdClass;
        $c->test = function() { return 'hello world'; };
        echo ($c->test)();

~~~
luckylion
It's less obvious though, you need to know what's happening and why. Likely
not a problem for somebody writing a lot of JS, but I rarely see similar
constructs in PHP code.

~~~
rossriley
I guess it's a little less known but you also have to use the same technique
to immediately invoke a method on a constructed object, eg.

    
    
        $response = (new Response())->handle ();
    

So it's not completely alien in PHP.

~~~
luckylion
That's true, and probably depends on the code base.

I don't know how these patterns develop - it's very common in JS, but somewhat
rare in PHP. Maybe it has to do with the average level of the programmer in
each? Or jQuery's noConflict() helped it along with everybody starting with
(function($) {})(jQuery), thus integrating IIFE into their active repertoire.

I don't remember (and haven't found in quickly searching) PSR saying anything
about that, but that could explain a difference in usage, too.

~~~
kijin
Most PHP codebases simply don't have much use for IIFE's. A well-maintained
project will be neatly organized into namespaces and classes, and undefined
variables don't automatically become global. Meanwhile, PSR-1 says that a file
that defines classes and/or functions should not have any side effect. So you
don't have to worry about accidentally interacting with other scopes.

Anonymous functions and classes are being used more and more, but PHP isn't
fundamentally event-driven like JS so there's less need to hand them out like
candy and nest them several levels deep. Most of the closures I do use on a
daily basis are callback functions to things like array_filter() and
preg_replace_callback(), not event handlers like every jQuery code ever
written.

------
scotty79
I'm happy to see more languages with optional typing.

Prototyping stuff without bothering with types and adding them when your code
matures to root out some bugs and make it more futureproof seems to me like
the best of both worlds.

~~~
superfist
I will never undestand this argument. Bothering with types? How? In most cases
you will need int, double or string - how this is difficult? It is more
difficult to figure out types you heve to deal with if language doesn't
provide such information. If by prototyping you mean Hello World like apps
then ok, but anything more complex without types becomes mess very quickly and
prototyping speed have to decrease.

~~~
scotty79
If you really want to understand please find out why so many people prefer
dynamically typed languages. Maybe start using one yourself...

~~~
Risord
And what if you are used dynamically typed language for years and still cannot
understand why anyone would choose on if can chose freely?

Seriously I have noticed that static vs dynammic preference is quite
sustainable thing for people. Even if you are have used lot of both most
people prefer the one they are used when they 'got it' during their
programming learning path.

I don't really understand that CSV example what's the problem but one concrete
challenge I have noticed with static types is when you want do mutation which
effectively change the type. Instead of setting just new property you have to
potentially do dummy copy (including type) or some additional hashmap by id
for this additional data.

(btw I am not your down voter)

~~~
scotty79
I went back and forth between typed and untyped languages. The sheer drudgery
of typing out and visually filtering out while you read the obvious type
information is what makes typed language feel way less fun for me. When you
cut things out move them around, delete them, retype them, throw everything
away again and start over... I'd never choose Java for initial implementation
of anything.

I'm not sure why other people like dynamic languages but there must be some
advantages.

------
c487bd62
Is most of the PHP ecosystem moving to static typing? Libraries, ORM, etc.
When you're dealing with database objects, is everything a string?

~~~
joshlemer
This still isn't static typing, php type annotations are enforced at runtime.

