Rendered at 23:31:03 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
idoubtit 12 hours ago [-]
For anyone not familiar with PHP, static analysis is pretty much required because the official PHP type system is minimal. The detailed types are added through phpdoc comments, and they also help IDEs to provide a sane completion. For instance, the plain "array" should be detailed with phpdoc:
/**
* @param array<string, MyRecord> $input
* @return list<int>
*/
function foo(array $input): array
{
foreach ($input as $string => $record) {
// IDE knows $record is an instance of MyRecord
The static analysis will use both type systems, and it's the only way to check that phpdoc are consistent. Psalm has various levels of strictness, and even at a very lax level, it can help detect errors or code smells.
There are two major static analysis tools for PHP, Psalm and PHPstan, both written in PHP. The latter used to be much slower, but last time I tried it there was little difference (about 20 s for 100k lines + dependencies).
My guess is that a new contender, Mago, written in Rust, will probably soon replace those other tools. Its commands (analyze, lint) are already almost as complete as psalm/phpstan. Though the migration won't be easy because it does not have a notion of levels.
bel8 12 hours ago [-]
PHP type system is lacking but I wouldn't call it minimal.
PHP even has union types, just like TypeScript.
The major issue is the lack of array types and it's 90% of the reason I see projects using extra linting tools.
Maybe "minimal" was not the right word. I hope the context made clear that I meant the type system was lacking even for basic uses. Here are 3 common cases.
Some PHP functions (e.g. opening a file) return a `resource` type, but PHP has no official way to declare it. So the right way is to declare `mixed` as an official type (which means `any`) and fix that declaration with a phpdoc comment.
Phpdoc provides generics (named "templates", à la C++), e.g. `iterable<Something>` where `iterable` is an official PHP type (which is awfully imprecise without phpdoc).
Phpdoc has tuples and (associative) array shapes, e.g. `@return list{int, \Error}`.
In past PHP versions, the type system was worse because the internal types were ambiguous: types were implicitly nullable, i.e. `string` meant `null|string`. When PHP gradually added sum types, they fixed that ambiguity.
someothherguyy 8 hours ago [-]
> Phpdoc has tuples and (associative) array shapes, e.g. `@return list{int, \Error}`.
`list<Error>` a `list<T>` is `array<int<0, max>, T>` (some differences in phpstan vs psalm vs mago)
Timon3 11 hours ago [-]
> PHP type system is lacking but I wouldn't call it minimal.
PHP still doesn't have generics, right? IMO any type system without higher-order types is definitely on the minimal side.
final class Door {}
/**
* @template T
*/
final class Box
{
/** @param T $value */
public function __construct(private mixed $value) {}
/** @return T */
public function get(): mixed
{
return $this->value;
}
}
final class DoorInspector
{
/** @param Box<Door> $box */
public function __construct(private Box $box) {}
public function inspect(): void
{
$this->box->get()->open(); // analyser knows this is a Door
}
}
new DoorInspector(new Box(new Door())); // OK
new DoorInspector(new Box('not a door')); // error: Box<string> given
You can even bind a template by a generic type with "of":
final class Door {}
/**
* @template T
*/
class Box
{
/** @param T $value */
public function __construct(private mixed $value) {}
/** @return T */
public function get(): mixed
{
return $this->value;
}
}
/** @extends Box<Door> */
final class DoorBox extends Box {}
/**
* @template T
* @template F of Box<T>
*/
final class Shelf
{
/** @param F $box */
public function __construct(private Box $box) {}
/** @return F */
public function box(): Box
{
return $this->box;
}
/** @return T */
public function item(): mixed
{
return $this->box->get();
}
}
$shelf = new Shelf(new DoorBox(new Door()));
$shelf->box(); // DoorBox
$shelf->item(); // Door
Timon3 4 hours ago [-]
Yeah, static analyzers have been filling this gap quite nicely for long time, but GP was explicitly excluding those, no?
someothherguyy 8 hours ago [-]
yeah, it is a real pain in the ass. imagine if php 8 accepted the RFC for generics
wesammikhail 12 hours ago [-]
That's exactly it but PHP Core refuses to address the typed array issue in wait for a consensus on Generics even though those two things serve different audiences.
I haven't actually run into a single issue with PHP's type system in the last few years. Literally the only thing that remains is typed arrays but that seems to be ways off due to fragmentation in how the core devs want it handled.
hparadiz 12 hours ago [-]
With how good PHPDoc and Phan are these days I don't really think about it anymore. You can sort of have typed array by implementing Iterator, Countable, ArrayAccess, and optionally Indexing but I assume you mean defining types of array values.
Tadpole9181 8 hours ago [-]
If I could take a tangent and talk about the infuriating implicit-casting behavior of arrays in PHP?
The static analyzer will say this is safe (sans a bleeding edge setting in PHPStan), but it will throw a type error when it runs. Because the string '1' is, despite having strict types enabled, still implicitly cast to the number 1.
tredre3 3 hours ago [-]
Internally PHP has three distinct array types (the distinction is mainly for optimization purposes, it doesn't enforce anything): Regular arrays (continuous numeric keys), sparse arrays (all keys are integers but there can be holes) and hashmaps (anything goes).
I wish that the implicit casting you describe only happened in the two first case. In your example PHP already knows that $foo is a hashmap, so I'd want it to keep '1' as is!
Unfortunately for all of us, for every person surprised by this behavior, there's someone who relies on it. So it'll never change.
Best we can hope is to have better array types eventually (no, the SPL isn't it).
wesammikhail 6 hours ago [-]
I had this happen to me yesterday in spite of knowing about it because I was rushing. I added phone numbers as array keys to deduplicate the whole set. Some ended up as ints and some as strings.
PHP arrays are the most versatile part of the language. They're what actually makes the language so good in so many ways.
But typed arrays are a freaking requirement at this point and I've been screaming about it for years now... alas no response.
hparadiz 3 hours ago [-]
It's a performance optimization in this case. '1' is a string and you can hash it like all the other strings with PHP's fork of djbx33a or you can immediately store it at memory index 1. PHP tends to have a lot of old code using numeric strings so the performance benefit of not hashing all of those is obvious.
wesammikhail 59 minutes ago [-]
I never dug into it but I suspected memory optimization to be the reason. Thanks for confirming.
someothherguyy 8 hours ago [-]
pretty naive take, no run time guarantees, basically swinging on hope to keep your shit together. phpstan drops types on static analysis for deeply nested arrays/objects for example. or even those with many members.
someothherguyy 8 hours ago [-]
it has union and intersection types, but it is nothing like typescript.
littlecorner 3 hours ago [-]
A shiggaion of David
tosti 7 hours ago [-]
> An official Discord community!
That's a regression.
anthk 14 hours ago [-]
Thou shall use the Rust rewrite of PHP.
hiccuphippo 10 hours ago [-]
Why not skip the php and write rust directly?
anthk 6 hours ago [-]
Compatibility.
Y-bar 13 hours ago [-]
”Rewrite in Rust” fanboys are more annoying than crypto bros. How is such a feat even possible?
anthk 6 hours ago [-]
Check RustPython and PhpRust, which already can run WP.
scronkfinkle 11 hours ago [-]
I dunno,unlike the crypto/NFT era, Rust rewrites have demonstrated measured performance and memory savings gains. Some grimace when they see "written in Rust" as a tagline but my immediate thought tends to land more on the "thank God this isn't an electron app" side of things
Y-bar 10 hours ago [-]
Operative word is “fanboys”, I have mostly no qualms of actual rewrites to that language. It’s the fanboys who insist upon injecting themselves and their opinions everywhere with net negative impact.
tredre3 3 hours ago [-]
You're comparing the rust rewrite of a web-based desktop application with the rust rewrite of a compiler+interpreter/jit. The former improves things by removing 12 layers of abstraction and an entire browser engine. The latter, not so much, it's already bare-metal.
So as much as people on this forum love to shit on it, PHP is already very fast and memory efficient.
The gains from the ongoing rust rewrite are (in order):
1. The author's ego-stroking
2. Claude's agentic capabilities show-case
3. Memory safety (to be confirmed)
RunSet 10 hours ago [-]
Google funding, I suspect. One day they will turn off the tap and the mantra will be silenced.
"Strange; seems like all I hear about these days are projects funded by the world's largest advertising corporation."
wesammikhail 12 hours ago [-]
inb4 "Rewrite bitcoin in Rust" comments stars pouring in
origini 14 hours ago [-]
Ah boy o boy, PHP was the first lanuage I teached myself but man it still needs PHPDoc comment on return types?
Come on PHP has so many shitty things, but even something basic like this is still not in the lanuage itself?
pkkm 10 hours ago [-]
PHP does have return type annotations. The reason the PHPDoc comments are needed here is that it lacks generics. Unfortunately, adding generics has proven problematic, but I still hope that we'll see them in the next few years.
monster_truck 14 hours ago [-]
It's had return type declarations since 7. There's even intersection and literal types now. You could have typed this into google and it would have told you
origini 13 hours ago [-]
I did and it told me that it doesn't support typed arrays.
jasonlotito 7 hours ago [-]
Man that goal post moved quick.
origini 3 hours ago [-]
The example on the Psalm 7 page (https://psalm.dev/) showed the PHPDoc return type.
I was not precise enough about the exact version of it, fair, but my correction did not change my opinion on this topic.
And the PHPDoc stuff is old, it was normal to write them everywere 11 years ago and still today you have to write them for some cases.
Its still inconsistent and stupid.
bakugo 13 hours ago [-]
Type declarations are still quite limited, though. For example, you can declare that a function returns an array or a string, but not an array of strings.
lofaszvanitt 13 hours ago [-]
Use a class if you need them.
bakugo 12 hours ago [-]
How does that solve the problem? You can't declare an array of strings inside a class, either.
eithed 9 hours ago [-]
You can annotate the method as `@return array<array-key,string>` or even `@return list<string>` (array of strings = indexed via anything that can be used as a key in PHP vs list of strings = indexed via incrementing integers, starting from 0). I guess the OP suggestion was to use Arrayable / Iterable interfaces, which would work as well, but would require more effort (= yes, declare a class)
lofaszvanitt 11 hours ago [-]
Do some research :D.
origini 11 hours ago [-]
Just to be clear, you suggest to wrap a typed array into its own class? is that the suggestion you think of?
Because if yes, thats just ugly and a hack/workaround.
Y-bar 14 hours ago [-]
TFA even shows examples of native return types. How could you not see that?
origini 13 hours ago [-]
The page itself shows plenty of examples were this is not true.
Including generic type arrays which is not supported.
throw1234567891 13 hours ago [-]
"Taught", not "teached".
spiderfarmer 13 hours ago [-]
"Cool! A PHP article on HN, let's rehash some of my comments that got lots of upvotes 11 years ago!"
origini 13 hours ago [-]
Yepp, especially because not much changed and other languages did quite a lot. Strong contrast don't you think?
ooh wait, you couldn't be more wrong. Pikachuface.png
origini 12 hours ago [-]
Core issues have not been fixed.
You remember when HHVM / hack came out from Facebook? Besides the fact that it was quite a dick move from Facebook to bring it out and then stop supporting it, it pushed PHP to take performance temporarily serious and there was so much air in it.
But the standard library is still inconsistent as hell, type system is still garbage especially mixxing strict and non strict.
The concurrency implementation? Has to be fixed again by frameworks.
And yeah i mean the critisism of my original comment stands as well and thats just an eye sore.
There are two major static analysis tools for PHP, Psalm and PHPstan, both written in PHP. The latter used to be much slower, but last time I tried it there was little difference (about 20 s for 100k lines + dependencies).
My guess is that a new contender, Mago, written in Rust, will probably soon replace those other tools. Its commands (analyze, lint) are already almost as complete as psalm/phpstan. Though the migration won't be easy because it does not have a notion of levels.
PHP even has union types, just like TypeScript.
The major issue is the lack of array types and it's 90% of the reason I see projects using extra linting tools.
[1] https://php.watch/versions/8.0/union-types
Some PHP functions (e.g. opening a file) return a `resource` type, but PHP has no official way to declare it. So the right way is to declare `mixed` as an official type (which means `any`) and fix that declaration with a phpdoc comment.
Phpdoc provides generics (named "templates", à la C++), e.g. `iterable<Something>` where `iterable` is an official PHP type (which is awfully imprecise without phpdoc).
Phpdoc has tuples and (associative) array shapes, e.g. `@return list{int, \Error}`.
In past PHP versions, the type system was worse because the internal types were ambiguous: types were implicitly nullable, i.e. `string` meant `null|string`. When PHP gradually added sum types, they fixed that ambiguity.
`list<Error>` a `list<T>` is `array<int<0, max>, T>` (some differences in phpstan vs psalm vs mago)
PHP still doesn't have generics, right? IMO any type system without higher-order types is definitely on the minimal side.
One can declare:
You can even bind a template by a generic type with "of":I haven't actually run into a single issue with PHP's type system in the last few years. Literally the only thing that remains is typed arrays but that seems to be ways off due to fragmentation in how the core devs want it handled.
I wish that the implicit casting you describe only happened in the two first case. In your example PHP already knows that $foo is a hashmap, so I'd want it to keep '1' as is!
Unfortunately for all of us, for every person surprised by this behavior, there's someone who relies on it. So it'll never change.
Best we can hope is to have better array types eventually (no, the SPL isn't it).
PHP arrays are the most versatile part of the language. They're what actually makes the language so good in so many ways.
But typed arrays are a freaking requirement at this point and I've been screaming about it for years now... alas no response.
So as much as people on this forum love to shit on it, PHP is already very fast and memory efficient.
The gains from the ongoing rust rewrite are (in order):
1. The author's ego-stroking
2. Claude's agentic capabilities show-case
3. Memory safety (to be confirmed)
"Strange; seems like all I hear about these days are projects funded by the world's largest advertising corporation."
Come on PHP has so many shitty things, but even something basic like this is still not in the lanuage itself?
I was not precise enough about the exact version of it, fair, but my correction did not change my opinion on this topic.
And the PHPDoc stuff is old, it was normal to write them everywere 11 years ago and still today you have to write them for some cases.
Its still inconsistent and stupid.
Because if yes, thats just ugly and a hack/workaround.
Including generic type arrays which is not supported.
ooh wait, you couldn't be more wrong. Pikachuface.png
You remember when HHVM / hack came out from Facebook? Besides the fact that it was quite a dick move from Facebook to bring it out and then stop supporting it, it pushed PHP to take performance temporarily serious and there was so much air in it.
But the standard library is still inconsistent as hell, type system is still garbage especially mixxing strict and non strict.
The concurrency implementation? Has to be fixed again by frameworks.
And yeah i mean the critisism of my original comment stands as well and thats just an eye sore.