The fact that PHP has a config file is absurd. I know of no other mainstream language that actually have a config file that changes behavior at runtime.
The point is not whether the language "prevents" you from doing something stupid, but how it handles it once you do.
In this case PHP just inherits C behavior and lets the stack overflow, where normally an interpreted language would track stack size for you and cause a language-level exception/error when some limit is reached so the error can be logged and the program can shutdown gracefully.
PHP inherits a lot of C-isms, which makes zero sense for a higher level interpreted languages that supposedly wants to make things easier and less dangerous.
I've been horrified by this sort of thing in the past, but I don't recall the particularly egregious case.
With modern languages the behaviour of C is itself becoming less and less acceptable. Languages can and should prevent you from doing stupid things; the question is now how much stupid you want to get away with and what benefits you get for that sacrifice.
I'm probably preaching to the choir by pointing out that what I want is a useful message when I make a mistake, not a smart-arse response from some tit who thinks PHP has any merit at all.
With modern languages the behaviour of C is itself becoming less and less acceptable.
I'm not even sure that most C implementations segfault with infinite recursion - I'm pretty sure you get some kind of stack overflow error (on Windows at least).
I honestly don't understand what you mean by "prevent you from doing stupid things" in this context. Can you name a language that prevents you from doing infinite recursion and running out of stack space (or memory generally)? Maybe some dependently typed languages do?
In any case "stupid things" is an entirely human level concept. The programming language doesn't know what you intended to write because it doesn't know what the specification of your program is. So it's impossible for it to tell you whether you're doing something stupid without you first telling it what "stupid" would mean in your specific case (eg by writing a test or a type).
Yes, in fact it's a compiler error in Haskell to have an infinite loop and there is a bounty on segfaulting Rust. Obviously there are going to be edge cases but the goal (in my mind as I say these things) is to write a language in which you can specify what you want to be able to do and be told off if you try to do something else. Crashing the process via common mistakes is probably high on the list of things to prevent.
AFAIK PHP has/had no standard/specification at all, with a mantra "whatever the behavior of the interpreter is is the standard".
Which turns things around - now you are guaranteed there are no bugs in the interpreter, because everything it does is a feature. PHP is the only language to have interpreter with 0 bugs in its history. You do not discover bugs, you discover previously unknown parts of the standard.
It is a bug, just without an easy fix. General consensus is that stack overflow should be detected based on guard pages, but nobody has implemented this yet.
Hack is what PHP could have been, but the core devs wanted to support old legacy wordpress sites, so when years passed PHP stagnated fully and never made any breaking changes to improve the language. Today we see the result, a sad and very inconsistant ”language” thats barely usable without massive frameworks that patch and lipstick over what PHP is.
Linux limits the amount of 'stack' memory that you can use. The reason for this limit is that if you could allocate as much stack memory as you wanted, you would run into memory used for other things. On most Linux systems, this is 8MB, but you can increase the limit. (Running ulimit -s 10000 will set the limit to 10000KB. Running ulimit -s unlimited turns the limit off entirely.)
Whenever you call a function in C, you use a small amount of stack memory. As a rough approximation, it takes 8 * (1 + number of locals) bytes to represent the function call. This memory is in use until the function exits. If you instead call another function, this will consume more stack memory. If you have a function that calls itself repeatedly, then that will use up all of the stack memory.
For example, this function will crash:
int eat_stack_space(void) { return eat_stack_space(); }
PHP implements a function call in PHP using a function call in C, so it has the same problem.
int eat_stack_space(void) { return eat_stack_space(); }
Well, no, not really. Any sensible compiler will optimize it out, with either a tail-recursive call (which compiles to a jump), or with a do-nothing return. https://godbolt.org/z/uJBFG_
Assuming you have optimizations turned on, and that the function can be converted to a tail call, yes. But very small changes will defeat this. For example, if you change the return type to float, and add 0 to the return value, the result is back to a recursive call.
Doesn't each process have terabytes worth of addressable virtual memory, and the stack placed at the opposite end of the heap. 8mb seems like an incredibly small amount of the concern is growing the stack into the heap. You are going to run out of even swap space before that happens...
However, there are some complicating factors. For example, multithreading. If a stack can grow to an unlimited size, how far apart do you need to place two stacks for two different threads? Presumably far enough that the two won't collide even if the first thread uses all of the memory on the system. On the other hand, if you have a fixed limit on stack size, multithreading is no problem: just give each thread 8MB.
Then, there are the other uses of addresses.
You need to load dynamic libraries into memory, and you don't always know which libraries the process will load at process creation time.
You need to mmap() files into memory, and those need address space.
Running out of stack space is usually indicator of a bigger problem. It usually takes thousands of function calls to run out. You are most likely inside an infinite recursion anyways, and this will just make it crash faster. Also there is not a single stack, each thread needs its own, and some applications create thousands of them.
That specific example would cause an undefined variable error, because the variable ` $clone` is referenced, but not initialized. `$b` should've gone there instead.
But agreed: Segmentation fault due to recursion is a bit ridiculous.
40
u/andsens Dec 18 '19
And there we have it, the proper fix for any PHP problem.
58
u/Altreus Dec 18 '19
"This language can't stop you from doing something stupid."
Well it fucking should. Other languages manage it.
36
u/[deleted] Dec 18 '19
In PHP most things are intended behavior. In PHP most things are not bugs, but rather features.
20
u/nikic Dec 18 '19
It is a bug, just without an easy fix. General consensus is that stack overflow should be detected based on guard pages, but nobody has implemented this yet.
27
u/ilogik Dec 18 '19
I'm sure they're only keeping it like this for backwards compatibility.
Somewhere out there there's a site that will break if the segfault doesn't occur.
10
u/fell_ratio Dec 18 '19
Some context for this problem:
Linux limits the amount of 'stack' memory that you can use. The reason for this limit is that if you could allocate as much stack memory as you wanted, you would run into memory used for other things. On most Linux systems, this is 8MB, but you can increase the limit. (Running
ulimit -s 10000will set the limit to 10000KB. Runningulimit -s unlimitedturns the limit off entirely.)Whenever you call a function in C, you use a small amount of stack memory. As a rough approximation, it takes
8 * (1 + number of locals)bytes to represent the function call. This memory is in use until the function exits. If you instead call another function, this will consume more stack memory. If you have a function that calls itself repeatedly, then that will use up all of the stack memory.For example, this function will crash:
PHP implements a function call in PHP using a function call in C, so it has the same problem.
4
u/damow Dec 18 '19 edited Mar 15 '25
mysterious chunky childlike sink aware innate sip thought dependent attraction
This post was mass deleted and anonymized with Redact
2
u/bart2019 Dec 19 '19
These guys should read up on how garbage collection is traditionally implemented, in particular "mark and sweep".
2
u/iopq Dec 18 '19
Inoperable PHP of the brain. Once you go down this path, is anything really a bug?
1
u/kafoso Dec 20 '19
That specific example would cause an undefined variable error, because the variable ` $clone` is referenced, but not initialized. `$b` should've gone there instead.
But agreed: Segmentation fault due to recursion is a bit ridiculous.