Re: [PHP-DEV] RFC: Nested enclosing returns
On 21 March 2015 at 12:13, Georges.L cont...@geolim4.com wrote: Hi php internals, After some long and deep research i finally decided to write my first RFC about a feature i'd be interested to be improved in the php core: *Nested enclosing returns* The main purpose, as the title say, is to have the possibility to nest multiple return like we can do currently with break/continue. I thinks it'll being better with a scheme: function foo($foo = true) { if(!$foo) { return false, 3;// Please note the second return argument is the return nesting level } else { return true;// Default nested return argument is always equal to 1 (if not specified, current) } } function bar($foo = true) { foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 2 echo 'Hi jon !'; } function baz($foo = true) { echo 'Hi bertie !'; foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 3 echo 'Hi freddy !'; } baz(true); // Display: // Hi bertie ! // Hi jon ! // Hi freddy ! baz(false); // Display: // Hi bertie ! Benefits: -Wont break compatibility -May improve code interpretation speed due to part of code skipped by the nested return. -Allow dynamic return expressions. Inconveniences: -May complicate debug/backtrace I think this will lead to a debugging nightmare, sorry. It allows functions to behave in an incredibly non-obvious way, makes it really easy to break things, and when something is broken the complexity of locating and fixing it feels like it will be insane. I'm imagining functions with branches that have different return levels in them, called from functions with branches with different return levels in them I feel like crying and it's not even a real feature yet!
Re: [PHP-DEV] RFC: Nested enclosing returns
Am 21.03.2015 14:15 schrieb Georges.L cont...@geolim4.com: The main purpose, as the title say, is to have the possibility to nest multiple return like we can do currently with break/continue. I think that is a complete nonstarter. Functions are reusable building blocks, designed to be called from various places that the function author should not be concerned with. break/continue can always be analyzed at the single function level, with semantics unambiguously given by the local nesting level within the function. On the other hand, functions being called from various places means the nesting distance between a given point in the outer program, and the function itself, is completely arbitrary for different calls. In some places in our codebase we have logging functions that try to show logically useful debug_backtraces, by leaving out a number of lowest levels of the stack. Starting down that road soon required us to introduce $up = 2 parameters to the inner logging functions, so that different call pathes could impose how much to leave out (think some convenience wrappers). This is a very brittle thing to do. Okay for debug backtrace logging, because at worst it shows some unneccessary level or leaves out some. But totally unmanageable when it affects really control flow. I know exactly one language that provides similar stuff - TCL with its uplevel and upvar constructs - and anybody who had to wrap their heads around code using that, nifty as it can be, will know the cognitive strain involved... Patrick
Re: [PHP-DEV] RFC: Nested enclosing returns
On 21 March 2015 17:55:32 GMT, Georges.L cont...@geolim4.com wrote: To be honest I had not thought about the bad side of this use, i guess it could be possible to not return upper than a try/catch block (then return a fatal error if our code is trying to returns upper than a try/catch block). Now the practical example: // Code i have the hand on function main() { foo('bar'); echo I'm an angel !; } function bar() { echo I'm not Evil; } //Code i don't have the hand on (like a MVC core) function foo($thing) { // make_love_dont_war(); call_user_func($thing); //Do some stuff i don't want to execute echo 'evil'; } function make_love_dont_war() { echo 'Make love, dont war.'; } You get it ? Not really, no. In this example, the code you want to skip happens to be all the code after executing the callback, but what if there was some essential cleanup there, or the evil line was before the callback, not after? In general, a function should be a reusable, self-contained unit of code, and as such shouldn't want to know about where it was called from. But in order to know how many levels to force to return, you need to know how deeply nested you are, and the expected return type of the function you're jumping out of, as well as which code you're bypassing by doing so. If someone reuses the function, or refactors the calling code, some very confusing bugs will result. Rather than jump N levels up the call stack, what you maybe want is jump up to somewhere labelled X. This is sort of what exceptions do - they jump up the stack until they find somewhere expecting to handle that condition. The function throwing the exception doesn't know where it will be caught, so is not fragile to reuse or refactoring. However, using exceptions for anything other than error handling is generally frowned upon, because they can lead to hard to follow logic which would be better off refactored in plain procedural/OO style. In your example, it's up to the maintainers of the foo() function (e.g. framework) to make the evil optional, by passing in some additional setting, or allowing the callback to return a special value, or just splitting the work in such a way that you can access the desired functionality without calling foo() at all. If it's open source, you could submit a patch, or, worst case, maintain a fork. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] RFC: Nested enclosing returns
On Mar 21, 2015, at 10:17, Georges.L cont...@geolim4.com wrote: The main purpose of this RFC is *not* to improve the exception system of PHP but to improve the code logic/hierarchy. Hi php internals, After some long and deep research i finally decided to write my first RFC about a feature i'd be interested to be improved in the php core: *Nested enclosing returns* Georges, This would make simply looking at code and reasoning about what it does impossible. At present, if I have the following code: function foo() { if(doSomething()) { success(); } else { failure(); } return 42; } try { bar(foo()); } catch($ex) { } Then I can make the following true statements about this code: * foo always calls doSomething() * foo always calls either success() or failure(), based on the result of doSomething() * foo always returns 42 * bar is always called (with foo’s return value, 42) * Alternatively to the above, any of the called functions may throw an exception, which will be caught by the catch block If any of doSomething(), success(), failure(), or bar() can arbitrarily return to some higher calling scope, then the only thing I can say for sure is that doSomething() is called, after which my application could be in some dangerously inconsistent state because I have no idea what will be executed next. This then provides significant security concerns. For example, if we have this: function API_Function_With_Callback($callback) { try { $callback(); //do more stuff return true; } catch($ex) { //do error stuff return false; } } function doEvil() { $sentinel = //some unique value $result = API_Function_With_Callback(function() use($sentinel) { $backtrace = debug_backtrace(); $nestingLevel = //determine nesting level from backtrace if($nestingLevel == 2) return $sentinel, 2; else if($nestingLevel == 3) return $sentinel, 3; else if($nestingLevel == 4) return $sentinel, 4; // etc } // Exploit inconsistent state of Call_API_Function here if($result === $sentinel) { … } } Then we can short-circuit code from some other library which isn’t prepared to deal with this kind of hijacking. More seriously, this sort of hijacking *can’t* be defended against (at least not without a weakening of your original proposal). Any function that takes a callback is potentially vulnerable to this sort of attack. Can you suggest an actual, practical, example, where this would be such a benefit as to override the inherent difficulty about reasoning about this code, and the potential security concerns? Are there any other languages that make something like this possible? I suspect that any code that could be “improved” with this functionality is already in significant need of improvement by more conventional means. -John -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] RFC: Nested enclosing returns
To be honest I had not thought about the bad side of this use, i guess it could be possible to not return upper than a try/catch block (then return a fatal error if our code is trying to returns upper than a try/catch block). Now the practical example: // Code i have the hand on function main() { foo('bar'); echo I'm an angel !; } function bar() { echo I'm not Evil; } //Code i don't have the hand on (like a MVC core) function foo($thing) { // make_love_dont_war(); call_user_func($thing); //Do some stuff i don't want to execute echo 'evil'; } function make_love_dont_war() { echo 'Make love, dont war.'; } You get it ? Georges.L 2015-03-21 17:07 GMT+01:00 John Bafford jbaff...@zort.net: On Mar 21, 2015, at 10:17, Georges.L cont...@geolim4.com wrote: The main purpose of this RFC is *not* to improve the exception system of PHP but to improve the code logic/hierarchy. Hi php internals, After some long and deep research i finally decided to write my first RFC about a feature i'd be interested to be improved in the php core: *Nested enclosing returns* Georges, This would make simply looking at code and reasoning about what it does impossible. At present, if I have the following code: function foo() { if(doSomething()) { success(); } else { failure(); } return 42; } try { bar(foo()); } catch($ex) { } Then I can make the following true statements about this code: * foo always calls doSomething() * foo always calls either success() or failure(), based on the result of doSomething() * foo always returns 42 * bar is always called (with foo’s return value, 42) * Alternatively to the above, any of the called functions may throw an exception, which will be caught by the catch block If any of doSomething(), success(), failure(), or bar() can arbitrarily return to some higher calling scope, then the only thing I can say for sure is that doSomething() is called, after which my application could be in some dangerously inconsistent state because I have no idea what will be executed next. This then provides significant security concerns. For example, if we have this: function API_Function_With_Callback($callback) { try { $callback(); //do more stuff return true; } catch($ex) { //do error stuff return false; } } function doEvil() { $sentinel = //some unique value $result = API_Function_With_Callback(function() use($sentinel) { $backtrace = debug_backtrace(); $nestingLevel = //determine nesting level from backtrace if($nestingLevel == 2) return $sentinel, 2; else if($nestingLevel == 3) return $sentinel, 3; else if($nestingLevel == 4) return $sentinel, 4; // etc } // Exploit inconsistent state of Call_API_Function here if($result === $sentinel) { … } } Then we can short-circuit code from some other library which isn’t prepared to deal with this kind of hijacking. More seriously, this sort of hijacking *can’t* be defended against (at least not without a weakening of your original proposal). Any function that takes a callback is potentially vulnerable to this sort of attack. Can you suggest an actual, practical, example, where this would be such a benefit as to override the inherent difficulty about reasoning about this code, and the potential security concerns? Are there any other languages that make something like this possible? I suspect that any code that could be “improved” with this functionality is already in significant need of improvement by more conventional means. -John
Re: [PHP-DEV] RFC: Nested enclosing returns
The main purpose of this RFC is *not* to improve the exception system of PHP but to improve the code logic/hierarchy. Using this like loop (for/foreach etc.) you can skip a lot of parent callers for a desired purpose by example. I agree with you in the sens were this can complicate significantly the debug/trace process, but IMO, there's more benefits than inconvenience. Also I agree with you too, were it can lead in a sort of spaghetti code feature (like Goto operator is...), but which language is not a bit spaghetti today? But my request is not to have a fully dynamic nested return, but a controlled dynamic nested return. I would not see this to be improved: return $something, $nesting_level; If I remember that feature has been removed in continue/break as of php 5.4, so this isn't that hard, right? :) Georges.L 2015-03-21 14:20 GMT+01:00 Benjamin Eberlei kont...@beberlei.de: On Sat, Mar 21, 2015 at 1:13 PM, Georges.L cont...@geolim4.com wrote: Hi php internals, After some long and deep research i finally decided to write my first RFC about a feature i'd be interested to be improved in the php core: *Nested enclosing returns* The main purpose, as the title say, is to have the possibility to nest multiple return like we can do currently with break/continue. I thinks it'll being better with a scheme: function foo($foo = true) { if(!$foo) { return false, 3;// Please note the second return argument is the return nesting level } else { return true;// Default nested return argument is always equal to 1 (if not specified, current) } } function bar($foo = true) { foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 2 echo 'Hi jon !'; } function baz($foo = true) { echo 'Hi bertie !'; foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 3 echo 'Hi freddy !'; } baz(true); // Display: // Hi bertie ! // Hi jon ! // Hi freddy ! baz(false); // Display: // Hi bertie ! Benefits: -Wont break compatibility -May improve code interpretation speed due to part of code skipped by the nested return. -Allow dynamic return expressions. Inconveniences: -May complicate debug/backtrace Hi Georges, In my opinion this is a bad idea. With break/continue/goto the jumps are restricted to only one function body to explicitly reduce the complexity in understanding them. With your proposal, as a user of a function I wouldn't know how deep a function returns. This can easily lead to extremely unexpected behavior. This is why exceptions are better for jumps up the call-stack, because they give the caller an explicit way of either accepting or catching the jump. Regards, Georges.L
Re: [PHP-DEV] RFC: Nested enclosing returns
On Sat, Mar 21, 2015 at 1:13 PM, Georges.L cont...@geolim4.com wrote: Hi php internals, After some long and deep research i finally decided to write my first RFC about a feature i'd be interested to be improved in the php core: *Nested enclosing returns* The main purpose, as the title say, is to have the possibility to nest multiple return like we can do currently with break/continue. I thinks it'll being better with a scheme: function foo($foo = true) { if(!$foo) { return false, 3;// Please note the second return argument is the return nesting level } else { return true;// Default nested return argument is always equal to 1 (if not specified, current) } } function bar($foo = true) { foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 2 echo 'Hi jon !'; } function baz($foo = true) { echo 'Hi bertie !'; foo($foo); // Some stuff that will never be executed if $foo == false and nested return argument = 3 echo 'Hi freddy !'; } baz(true); // Display: // Hi bertie ! // Hi jon ! // Hi freddy ! baz(false); // Display: // Hi bertie ! Benefits: -Wont break compatibility -May improve code interpretation speed due to part of code skipped by the nested return. -Allow dynamic return expressions. Inconveniences: -May complicate debug/backtrace Hi Georges, In my opinion this is a bad idea. With break/continue/goto the jumps are restricted to only one function body to explicitly reduce the complexity in understanding them. With your proposal, as a user of a function I wouldn't know how deep a function returns. This can easily lead to extremely unexpected behavior. This is why exceptions are better for jumps up the call-stack, because they give the caller an explicit way of either accepting or catching the jump. Regards, Georges.L