What's new in PHP 8?
stitcher.io
stitcher.io
I use this one program called Sendy (a PHP app) to help manage my email list. It's a typical LAMP set up.
It's been running on a $5 / month server uninterrupted for 905 days with no down time related to the app malfunctioning. The whole system uses around 300mb of memory.
Yesterday I decided to spin up a new server to finally retire the old one since it was using Debian Jessie that just went EOL 2 weeks ago. This required migrating a ~3 year old version of Sendy to the newest one (dozens of database schema changes included) and export / import my entire database.
Everything just worked and all I had to do was rsync the PHP files to a directory and access the site in a browser, and it was upgraded and ready to go.
I even ran into what I thought was a bug because my existing forms weren't working, so I looked into the single PHP file that was the form's action and within 5 minutes determined I was missing a hidden field (something Sendy added in a recent version). That was the first time I ever looked at its source code and PHP in a long time.
It very much reminded me of my PHP cowboy days. No complicated build process, no dependencies (the whole app uses PHP's standard library), no having to restart the server between deploys, etc.. I know there's downsides to that and I'm very aware of them, but after having worked with increasing complexity for the last 10 years of web dev, man it felt nice for a change.
I would quit working in software dev and become a fucking gardener before I would work on Wordpress projects all the time, and Sendy makes Wordpress look well written.
Edit: cowboy is a very apt term for Sendy. If the cowboy were drunk, and tried to make babies with the cows.
Did you run into bugs with it that required patching? I haven't to my knowledge.
It also tries to use a MySQL database as a queue/cache which is (predictably) terrible at larger volumes when you have eg a db cluster tuned for durability rather than raw throughput.
Want to fix a piece of logic (ie add tls support to the db connection)? Well you have a couple of dozen files to make the same change in.
And no i can’t link to it because it’s proprietary, and the really ridiculously bad code is even “obfuscated”, in a manner.
So you're saying you've never worked with Magento?
If it has any one of those things it’s already better than Sendy. Sendy literally (and I mean literally not figuratively) looks like someone used stack overflow and copy/paste instead of any kind of education or experience writing code.
As for NPM and the JavaScript packages, this is a flipping nightmare and I hate working with it!
I've been working almost exclusively with javascript and typescript for the last 4-5 years; so that now, while trying to write an amateurish Symfony project (because shared hosting) and appreciating the maturity of Symfony, I still hate almost every moment of it and hope I could have used typescript.
Anyway, what's wrong with npm and how is it so different than composer?
The way npm install always check latest package version and use them, even if package lock already exists. Which broke apps build many many times due to developer does not recognize this behavior. I know there are npm shrinkwrap and npm ci. Its just other package manager usually follow lockfile on install and have other command to upgrade the lockfile.
Also npm install download gazilion file for each dependencies. With deep directories.
In the past, there are problem because npm way of installing dependencies and hitting this infamous windows file path length limitation.
All are now already fixed or have alternative. But at that time, composer definitely much simpler and reliable.
It's good they fixed it I suppose, but yikes. As much as people say PHP has advanced from its fractal of awfulness days, a lot of those fixes are shocking if you come from other languages.
And a string that doesn't look like a number at all is converted to 0.
No, I'm not condoning this madness.
The PHP team is calling it "saner numeric strings"...seems to indicate they agree.
(int)"9" === 9
(int)"a" === 0
I assume this will cause backward incompatibility? I could see people relying on this instead of is_numeric. Not sure I like this change, since it introduces multiple ways of type casting (and therefore complexity). Before, it was obscure but simple, now, it'd be both obscure and not simple.
EDIT: It will cause backward incompatibility https://wiki.php.net/rfc/string_to_number_comparison
I'm surprised this was 44 against 1 in the vote. Seems like it's very likely to cause issues. IMO they should just implement a "strict comparison" mode, where comparing different types throws a runtime exception. Or just deprecate "==" for "===".
On the other hand, seems very much like the kind of crap that you're better to rip the band-aid and fix, than to keep for an eternity lest the fix causes issues...
>Or just deprecate "==" for "===".
As if this would cause less issues?
>As if this would cause less issues?
Causing issues isn't the problem. Transparently breaking code is. Deprecating would give time for people to adapt their code and would warn them about the downsides or loose type conversion. Changing the semantics of loose comparison will make static analysis and finding potential issues way harder (and may post-pone finding the problems it introduces to years down the line). Adding a mode that warns/throws when encountering loose comparison would both make current code still work, and warn users of potential issues.
Not for silent failure. This soft of change means that tons of systems will fail in unexpected ways in unexpected places, and often people will never know about it. It's a security nightmare.
Ripping the band-aid off is much more compelling when it will result in an exception. Silent failure, though. Ugh, if you actually care about writing reliable software, it's the worst.
Yes but I assume codes that will be broken by this are already broken.
If you look hard enough, you'll probably find some site that shows surprising behaviors of your favorite language. Examples:
Python: https://github.com/cosmologicon/pywat
Javascript: https://loomcom.com/blog/0097_the_wats_of_javascript.html
Ruby: https://idiosyncratic-ruby.com/29-limitations-of-language.ht...
`parseFloat("foo") == parseFloat("foo")` and `parseInt("foo") == parseInt("foo")` in Javascript evaluate to false which is the sane thing to do. `NaN != NaN` is part of the IEEE 754 floating point standard, which predates every single one of the languages you mentioned.
We're all competing in the olympics of suffering, so none of us should be promoting this shit :)
https://en.m.wikipedia.org/wiki/Undefined_behavior#Examples_...
https://medium.com/@mikehall314/wat-s-happening-a-closer-loo...
jshell> Long a = 12l; Long b = 12l;
a ==> 12
b ==> 12
jshell> a == b
$3 ==> true
jshell> a = 54321l; b = 54321l;
a ==> 54321
b ==> 54321
jshell> a == b
$6 ==> false
¯\_(ツ)_/¯Terms like boxing and reference comparison come to mind but I can't stitch them into a narrative to explain this.
https://stackoverflow.com/questions/1700081/why-is-128-128-f...
If you explicitly ask for a new object (Long a = new Long(123l);) you will get a new object and the comparison will fail as expected.
The moral of the story is to always use .equals().
Of course the language can require an explicit cast, but in most cases it goes against the intuitiveness and simplicity that dynamic languages want to achieve.
(bool) 'x' // true
(int) true // 1
If a string is truthy in a boolean situation, shouldn't it also be truthy in a numeric one?The types form a hierarchy in my mind, from simple to complex: from boolean to integer, to float, to string, to compound types like arrays and objects. It seems like values should cast up and down the chain uniformly.
1. If you convert a string which has non-numbers to an integer, an error is raised
2. Truthiness should not exist as a concept. Just booleans and expressions that evaluate to booleans.
PHP's backwards compatibilty promise makes these more difficult questions, of course.
What kind of error to raise? Do you return a NaN? Throw an exception? Both have their uses.
Meh, it's actually quite useful in many situations.
My criticism isn't the valid criticisms of PHP, it's that no matter how much progress the language makes people are quick to find reasons to be dismissive.
The language is good, people do good work with it. Lots of good work. How Javascript became the golden child in a community that can't forgive PHP is beyond me.
It's perfectly valid to be dismissive of PHP and you should recognize that. Main reason: there's a metric ton of PHP 5.X websites out there who are never going to be upgraded. That alone is a reason enough to distrust PHP.
If the community makes a concerted effort to introduce automated upgrades of legacy PHP codebases to the newer (supposedly better) versions then I personally would become a fan.
Everybody can tout the newest and greatest. Working with -- and improving -- the legacy stuff is what earns the respect of many, myself included.
(Disclaimer: not a JS dev. You seem to imply some kind of irony that JS devs mock PHP but this is not the case with me at least.)
You are free to criticize the enormous amount of legacy codebases out there, but that is not the fault of PHPs core team and the community working to improve the language and ecosystem, and it doesn't make PHP a bad tool. Environment maintenance isn't the languages responsibility and PHP has been -very- careful with it's upgrade path. It's childs play to move between versions.
If anything you should be impressed at legacy PHPs robustness. How long will modern stacks last unatended? Barely a year in my experience before some CI or dependancy breaks and the whole tower crumbles. Try and run an NPM based build pipeline in two years and tell me how that automated upgrade path worked out.
The crucial difference here is that nobody ever will even attempt to upgrade the PHP 5.X websites to PHP 7 or 8, whereas a lot of people gradually upgraded from Win 3.1 to Win10 over the years.
Obviously I am not saying "don't bother". Judging by your strong words (which don't make your argument more compelling) you seem to be a fan so you do you, code all the PHP that you like. I am giving you a realistic take on why many skip PHP. You getting a bit emotional over this doesn't change my stance.
I have been visiting -- and repairing -- PHP 5.X websites for years. They are anything but stable. I did maintain Java mastodons for years and they have been both (a) stable and robust and (b) legacy. But PHP projects I was in long time ago -- far from it. Could be anecdotal evidence.
To not make this annoying for future readers: I am okay with legacy code sitting untouched (although I will argue it is usually more broken than you seem to imply), but I am not okay with advertising a language that definitely didn't help the reputation of the IT branch in the eyes of everybody else. PHP sits on a broken foundation.
I have a right to express my opinion and you have a right to express yours. Let's leave it at that since we aren't going to achieve anything here (except flags for non-constructive posts).
There is a strong, intelligent community backing PHP and working with PHP, and you are dismissing it for things out of their control I feel. You're not taking a realistic perspective on what starting a project with PHP is really like today. It's fast, it has easy deployability, and you can use all the software engineering best practices you so please. Instead you're implying that the abandoned projects from yesteryear are what PHP is about today.
During it's prime, the web exploded with PHP installations built by people who had never touched software before and may never have touched it again, that's what legacy PHP is. It can't be fixed by the PHP community because those installations aren't run by people who are involved in the PHP community. PHP has a very sane upgrade path, but the people who run those installs will never know about it or be interested in it.
So I know your sentiment and I sympathize with it.
I too was a bit too dismissive and I am sorry.
My point was more along the lines of the seeming fact that the PHP community shrunk and, as you said, the original people who made it popular are long gone, likely to never come back. Not sure if anything can be done about it. As a fan of a fringe language I know how it is.
For what it's worth, I only heard good things about Laravel and several other libraries (or frameworks? don't know) along the lines of big productivity boosts.
Here's to hoping that one day all of that will converge somewhere where all of us will have to deal with less BS.
If you on the other hand don't know what you are doing, you are going to shoot yourself in the foot in one way or another.
If you really have some dynamic type situation and you for some legitimate reason don't know if your variable is going to be an int or a string, you would just compare using the === operator, which is a standard and well-known tool and does not have this issue.
Reading data from an excel sheet. A1 is “foo”, A2 is blank. B1 is =A1, which evaluates to “foo”, and B2 is =A2, which evaluates to 0. If you want to aggregate according to column B, you have to compare 0 and “foo”.
if(false == something) {}
In this case it's a semi-obscure C programming style to avoid accidentally using = in an if statement. (I think it's silly, but a decent number of people do it). So I could easily see a case in PHP where someone writes... if(0 == $foo)
and $foo usually holds a numerical string, but maybe a user entered "" there and now it's cast to 0 and maybe that code path shouldn't be taken. void (*signal(int sig, void (*func)(int)))(int)
means; if you don't know C it's plain gibberish, impossible to fully understand.Everything in computers (and I may say, more in general in life) is based on assumptions made on knowledge we implicitly have. After a while, writing eq or == becomes natural and you stop mixing them up. These kinds of errors are frequent when you don't use something daily; for instance, after more than a decade I still keep forgetting Powershell's syntax because I don't use it often and when I do, I tend to do basic things.
The old-school style, where routes map to scripts or parts of scripts and a separate web server like Apache or Nginx sits in front of the app, feels harder to reason about (does this thing persist across requests?), harder to debug (where did that error message end up?) more brittle (did this fail because of my script or my web server config or something else?) and more dependent on configuration magic (i.e. various text files lying around governing the behaviour of things).
So although some of these changes look cool, I don't think it's the language itself that most needs improving.
I don't really agree, php routes are handled natively and very simply and easy to reason about using the .php extension.
Its the frameworkers that broke that design with clean urls and fancy complicated routers.
Php behind Apache or nginx is incredibly simple and easy to reason about.
Browser loooks up this file url, this file url runs as a script and its output is returned.
A bug in one file isn't propogated to all others so you can have highly reliable services with only partial failures from time to time.
Whereas a bug in one route of a golang server may take the entire service offline.
I do much prefer the deployment and shipability of a golang service, so much I am willing to give up the simplicity of PHP.
To each his own, I guess. I just find your position genuinely surprising.
The user-defined context is the only route into the application state and can be as wide or narrow as you need. So you can share persistent state deliberately, e.g. by having that context contain a pointer to a db conn, but this is always a conscious, opt-in choice, and errors like accidental reuse should be impossible unless you're doing something inadvisable like using global variables.
In 99.9% of cases in web development you want the PHP model of doing things. The 0.1% case is the asynchronous background job, that can be solved, maybe not as elegant as in other languages, but why optimize the edge case?
So anything that needs to be processed asynchronously, like say, sending out a few dozen emails or notifications, I just publish the task to the queue and that other container handles them.
This could be simplified even further if you use a pubsub service that can post data to a url as it comes in. Thus allowing the single php container to asynchronously process the tasks all in their own individual threads.
This didn't work for me as Google cloud's pubsub seems to absolutely flood it's target with requests seemingly ignoring 503 or any other error code and not backing off until it has dos'ed your server.
That said, I'll gladly take the guarantees of a type checker in exchange for giving up the guarantees of statelessness. It would be nice if there were a stack that could give me both.
Of course there are solutions in PHP-land, e.g. "run a job server" or "use Redis", but I find the overall cost of introducing more programs/services/components to be very high, in terms of learning curve, portability, maintenance and debugging.
I've spent the last few years replacing a crumbly, brittle, temperamental PHP/nodejs/Gearman/Redis/Nginx/Supervisor stack with a Go monolith so I'm sure that influences my opinion :-)
My understanding is that the go philosophy is very package independent and the availability of existing code to solve common problems is rather sparse.
Running a PHP project isn’t as easy as
git clone
make run
because of the web server. Difficult to configure, hard to share configurations between machines, differences between operating systems (and Linux distributions) where to put configurations, different ways to hook into php.Projects starting to rely on specific web server configurations for different application behavior, e.g. htaccess files.
Hard to debug when you need to special request handling like terminating the request but continue executing or continuously stream data (web server response cache).
I guess that is the price you pay for separating the application from the web server.
That said, having a Go service just be exposed to the wild wild internet may not be a smart idea. Take a look at this issue [1], it is perfectly clear that Go services (relying on net/http) still need babysitting by a competent reverse proxy. It is a case where Go's mantra about simplicity leads to leaky abstractions and just clearly immature solutions.
Also not talking about TLS certs, WAF and other concerns that also may bring nginx etc on board anyhow.
My guess as to why this issue is left open? Google has global timeouts set on all the network edges so they don't want each service to use its own. Whats good for Google overrides whats good for the supposedly open source project.
This is pervasive in most of their projects. Google open source is like the Reapers from Mass Effect series. They leave some crumbs behind to ensure the software ecosystem develops along desired paths. Not out of charity, they do it to ensure a successful harvest.
Another concern is that the lifetime of a PHP app is rather short, measured in milliseconds. This is actually a rather robust approach. Crashes, memory leaks, all sort of weird states are brushed away immediately to be replaced with fresh state for the next request. Apache/nginx is acting as a supervisor and allowing a lot of shit code live out it's days unnoticed.
Contrast that with long-running Golang processes. You won't get away with sloppy code there, raising he bar considerably. Running a Go executable without a monitoring supervisor is sketchy at best.
Networking is another example. And casting to interface while the built-in collections secretly use generics.
A good way to avoid all these problems is to use a language with great cross-platform support like Java or C#.
I think that this vague definition, right here, is the source of disagreement. Are we talking about small blog with comments?
Or are we talking about one of the services of a medium-sized company, that needs to provide a platform-agnostic API, and real-time updates to subscribed clients with websockets?
That being said, I would still prefer a monolithic application for a small blog with comments because I think it's easier to manage, deploy, encapsulate, test etc.
Modern PHP projects will have a single entry point that handles all requests, with path-based routing done in code.
And so the eventual proxying Go behind Nginx or Apache comes with it's own cost since Go is nowhere near as tested as PHP when it comes to server integration.
“Does this thing persist across requests” is ridiculously simple to answer in php because the answer is “no, unless you very explicitly told it to”.
In a go or java or whatever service you have to know if your “thing” is specific to the request handler, or if it’s part of shared state.
There is no reason that routing in a php app has to be done via mapping urls to php files, it’s a very common practice to have a front controller that bootstraps the app environment and in turn handles routing of the request to a piece of logic.
They wrote a new compiler, for performance reasons, and don't have any accurate benchmarks? Really?
You can read about it here: https://stitcher.io/blog/php-jit
> Starting with new features, remember that PHP 8 is still in active development, so this list will grow over time.
Something doesn't seem right here :)
EDIT: Actually, there are polyfills here: https://php.watch/versions/8.0/str_starts_with-str_ends_with
PHP APIs (both core and frameworks) suffer greatly from inconsistency. Union types in function/method signatures can greatly help static analysis. It is great that PHP gets this built in.
It's a pretty big achievement and avoids a lot of hassle for users as you don't have to maintain different versions of programming languages on a server.
There are dozens of breaking changes in this release alone: https://github.com/php/php-src/blob/96c7d42a3ca9d822a08b3dd4...
Some of the changes are sadistic. "The precedence of the concatenation operator has changed relative to bitshifts and addition as well as subtraction." Have fun debugging code that worked perfectly well for years and now produces unpredictable results.
Even if you wanted to push the 8.0.0 to production as soon as it came out, you'd still have until November to run your tests on the RC branch and report any mishaps to the development team.
The backwards compatibility is mostly targeted to major platforms (Wordpress, Drupal, Magento, Symfony, etc).
> Have fun debugging code that worked perfectly well for years and now produces unpredictable results.
This describes my everyday experience with any language or codebase.
The cause for the unpredictable results are often human-related though, not version-related.
> Some of the changes are sadistic. "The precedence of the concatenation operator has changed relative to bitshifts and addition as well as subtraction."
I would gladly remove any code that does concatenation and math/bitwise on the same expression relying on the operator precedence. Sadistic is the person who writes these.
More than 2000 packages were analyzed and only 5 instances of that particular use were found[1], all bugs. This is a good change.
> This describes my everyday experience with any language or codebase.
That is horrifying. That should absolutely not be normal. I don't know what you work with normally, but it's clear that we are working in completely different worlds.
Stuff just breaks. Sometimes I get lucky and the guy before me wrote a cool test that prevents me from having to start a debugger, but the contrary is often the reality. OSS stuff is usually better, but not much better (OS upgrades always break, for example).
These changes reflect how people use the language or what they already believe it to do.
I can't think of a single time that I've seen a bug exposed by language changes. I have seen dozens of bugs created by language changes, however. (Mostly crashes).
Granted, Pinboard is just a wrapper around some SQL queries, but that's what most PHP apps are. It was a big relief to find the migration so easy.
Now I don’t even recognize what’s going on when reading code in a language I’ve used for my entire software development career.
It’s like a new language.
I want to be done with tech some days.
I just want to do my job, not relearn a language I’ve used for 20 years.
Good thing is you usually don't have to, languages like php really try and makntain backwards compatability, so you _can_ keep doing what you're doing.
Even with some changes, companies rarely ride the wave and come onboard much later when major issues have been ironed out. Hell, there are PHP4 apps still out there
I am really looking forward to generics in PHP.
Having to setup routing the cumbersome way has been the bane of being productive in Laravel.
In some cases, web.php routes aren't automatically updated if you use `artisan serve`, so you have to restart that sometimes.
And of course, it means there is a lot of distance between the file in which the controller and function are defined and the route definition. The route definition is in "routes/web.php" and the controller will likely sit in "app/Http/Controllers/Something.php", so if your Laravel project is moderately sized, you'll be scrolling a bit if you don't find the editor tab.
It's easier to see all the routes in that file instead of searching for them in the controller, especially as you said, in a moderately sized project, I can't be bothered to search or check the controllers. (You can counter argue that I can just Ctrl+F it). When you open up the routes file, you can see all your definitions in one place, which might ease the onboarding of new devs for example.
The routes file for any moderately sized project will likely be sufficiently complex that you cannot simply Ctrl-F it.
The router we use, we setup in our bootstrap $router->set(“users”, users/listAll::class); and listAll is then only required when matched. Our controllers also receive a default route based on their namespacing so we only configure them if it varies.
In theory if you have APCu or any other cache setup, you can use that to store routing information outside PHP, or just write it into a file. So only the first access is slow, then no longer and you can still manually scan if you can't find a function, controller or if you don't know the file involved.
It would atleast make code a lot cleaner.
That is exactly how Flask works by using Python decorators, example:
@app.route('/hello')
def hello_world():
return 'Hello, World!'
That would let you access /hello in a browser. There's also abstractions in place to prefix URL namespaces to a group of functions and add behaviors too (like requiring authentication).It's nice, especially when the framework comes with an ability to get a dump of all of your routes / URL endpoints in a single command, along with what function it's associated to and the HTTP method. You end up not missing a single routes file that other frameworks have (Rails, Phoenix, etc.).
Attributes worry me. Annotations are generally an eye sore to me, but in some places could be justified. For the most part, outside of Symfony, they were not used much. I'm worried that some developer will go bonkers with them, create a nightmare, and that will be the next legacy code base I inherit 5 years down the line.
Named arguments is okay I suppose, but IDEs solve knowing the order of arguments. I guess the IDEs will implement an auto complete for named arguments? I guess thats okay, but it will require more vertical code if you care about your lawn and keep your code in the PSR margins.
All in all I think this is a solid major, not the quantum leap we got from 5 to 7, but these are good additions. I'm sure we'll see plenty of decent additions in the minors.
Named arguments are incredibly helpful for functions with a bunch of optional parameters, like setcookie() -- instead of
setcookie("cookiename", "value", 0, "", "", false, true);
to set httponly (the last argument), you can now skip over the unused parameters and write: setcookie("cookiename", "value", httponly: true);
Not only is this more concise, but it's also self-documenting! setcookie("cookiename", "value", , , , , true);
As I used in VB.Net 15 years ago, I have no idea if other languages (C#?) support thatAnother benefit is name parameters can replace the current pattern of passing a "parameter bag" array. Code checkers can catch mistakes here at time of compile, rather than runtime.
The negative of named parameters as implemented here is that the names are defined by the variable names in the function definition. So, renaming variables in the function definition will break code that calls the function using named parameters.
The new match construct fixes both of these and is also an expression instead of a statement, allowing for more expressive code.
These kind of features would make debugging harder as people just ignore the actual error object and write weird common error message which could be useless.
The empty set is the identity element for set union! It seems very weird to exclude it.
function voidy() : void {
return;
}
echo json_encode(voidy()); // -> "null"
so i'd say that for most purposes void ≡ {null}
a singleton set, more or less[1], sometimes called a "unit type". that's because a void function does return something, it's just entirely uninteresting :) that's different from an "empty" type impossible ≡ {} // or ∅
with no values.
there are no values of type `impossible`, so `foo() : impossible` can never return anything at all, like this: function foo() : impossible {
while(1){}
}
see mypy's `NoReturn`, Rust's `!`, Haskell's `Void`. it's used for giving a type to functions like `exit()`[1] kind of, because it won't let you `return null` explicitly.
Was hoping for some awesome mods for a refrigerator-sized computer...