The background of this story is pretty long, so to keep things short -- I know goto is bad, but I have no other choice, because... PHP lacks comma operator.
I have such pattern -- regular function with entry point given as label, and inside it a small lambda which needs to goto to that entry point. Something like this (incorrect code):
function water()
{
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;goto _Entry_point_2_0;})();
// ... some code
}
I cannot just jump across the function boundaries, so my next idea is to return the label from the lambda and use it as "value" for goto. Something like this:
function water()
{
_Entry_point_2_0:
// ... some code
goto (function() { /*...*/ ;return '_Entry_point_2_0';})();
// ... some code
}
This does not work. Evaluating entire goto as a string eval ('goto _Entry_point_2_0;') does not work either.
The crazy part is I know the label in advance, so you can ask why I cannot write entire function like that:
function water()
{
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;})();
goto _Entry_point_2_0;
// ... some code
}
The problem is in logic -- executing lambda and goto make 2 expressions now, not one. And I need to make it in one expression -- execute lambda and goto has to be packed in single expression.
I also cannot call recursively the main function because it is whole point of this work, to avoid recursive call :-).
What are the other ways to achieve this?
UPDATE 1 Maybe I rephrase -- what I would like to achieve with goto is continue my_function. Executed from or at the boundary of the inner function (i.e. lambda).
UPDATE 2 The main goal is to loop over the main function, so it is almost equivalent to:
function water()
{
_Entry_point_2_0: while (true)
{
// ... some code
continue (function() { /*...*/ ; return '_Entry_point_2_0'; })();
// ... some code
}
}
"Almost" because of two reasons. I still have the problem with labels exactly as before, and what's more now I have problem where to add breaks to the loop.
So you can't skip a loop iteration from within an anonymous/lambda function. But you could return a value, compare values in the main function and then skip the iteration from there. Sounds simple, right?
Edit: if you also want to break from the lamba, you can use the same strategy.
function water() {
$lambda = function() { /*...*/; };
while (true) {
// Call the lambda function and store the return value.
$return = $lambda();
// If the return value was 'skip this iteration', well... skip it.
// Note: I'd normally compare to false, null or similar.
if ($return == 'skip this iteration') {
continue;
}
elseif ($return == "we're done!") {
break;
}
// Do stuff here.
}
}
I need to make it in one expression -- execute lambda and goto has to be packed in single expression.
So from your last comment I now partly understand why you have this requirement. But it's still not entirely clear. You have written a script to inject this code into some pre-existing function(s) and don't know the surroundings. But are the goto labels already in place? In any case, perhaps your problem is as simple as this: you want the code to be a single line only. But that's possible, code can span only a single line in PHP. It's not the prettiest, but it works just fine.
function water() {
while (true) {
if (function() { /*...*/; }() == 'skip this iteration') { continue; /* Or goto */ }
// Do stuff here.
}
}
Perhaps you are trying to solving your attemted solution rather than your original problem. This question smells XY problem.
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;goto _Entry_point_2_0;})();
Looks for me like do while loop:
do {
// ... some code
} while ((function() { /*...*/ ; return $k !== 0;})());
Now applying an anonymous function like that is not allowed. In addition closure parameters need to be explicitly declared. Thus my solution needs to be written like this:
$k = 10;
$f = function() use (&$k) { /*...*/ ; return $k !== 0; };
do {
// some code
} while ( $f() );
If you want to have a "comma operator" you just make a function that takes any numbers of argumens and return the last:
function begin(){
return func_get_args()[func_num_args()-1];
}
begin(expression1, expression2, expression3); // ==> result of expression3
All of a function arguments gets evaluated so it does the same given that the arguments are not dependent on each other.
Im fairly sure this is impossible by design.
Annonymous functions require all "state" to be passed in via a use statement. You can not pass a label as a reference
PHP Parse error: syntax error, unexpected '_Entry_point_2_0'
(T_STRING), expecting '&' or variable (T_VARIABLE)
The function is running in its own scope with no reference to the label which means it would need to be passed back somehow.
goto docs have the following
This is not a full unrestricted goto. The target label must be within the same file and context, meaning that you cannot jump out of a function or method, nor can you jump into one.
its also invalid to pass a variable to goto
PHP Parse error: syntax error, unexpected '$goto' (T_VARIABLE), expecting identifier (T_STRING)
Though all of this is probably a good thing, as goto suggests the logic flow is wrong, You will be able to find a way to structure this that is more corect and does not require a go to.
Edit:
After update 2 why not do
if(your lambda() == something) {
Continue;
}
Sorry wrote the code on my phone. That seems to do the same thing and is much more readable
You can use recursion:
function myFunc($i = 0) {
if ($i < 10) { //or any other logic to prevent "infinite loop"
//Main code
myFunc($i++);
}
}
Related
I am confused about return statement , why we need to use return in end of function , for example
function test($a){blah;
blahh ;
blahhh;
blahhhhh;
return;}
What the use of return here? Function automatically terminates when all the statements executed , I think there is no use of return here , but this picture from http://www.w3resource.com/php/statement/return.php make me confused
So
Can someone please explain the use of return (when we not returning any value).
It depends on what you're trying to achieve.
If you write echo in several places, your code will get confusing. In general, a function that returns a value is also more versatile, since the caller can decide whether to further manipulate that value or immediately print it.
I'd recommend to stick to the convention and use return for a function.
You should check GuardClause.
Example:
function test() {
return 10;
}
$a = test(); // $a stores the value 10
echo $a; // Prints 10
echo $a + 5; // We may want to manipulate the value returned by the function. So, it prints 15.
For further reference, check What is the difference between PHP echo and PHP return in plain English?
In that context: You don't.
return breaks out of the function, but since it is the last statement in that function, you would break out of it anyway without the statement.
return passes its argument back to the caller, but since it doesn't have an argument, there is nothing to pass.
So it does nothing.
Don't assume that every piece of code you stumble across has a purpuse. It might be left over from an earlier version of the code where something else (which gave it meaning) has been removed. It might be written by someone cargo culting. It might be placeholder for future development.
"return" is important when you plan to call this function from other codes, it helps you to:
Know if the function works correctly, or not.
Obtain values from a function.
Make sure other codes are not executed after return.
It might be useless when the code is simple as your sample, let's make it more complex.
function test($a){
if(file_exists($a)){
if(is_file($a)){
return $a." IS A FILE\n";
} else if(is_dir($a)) {
return $a." IS A DIR\n";
} else {
return $a." EXISTS, BUT I DONT KNOW WHAT IT IS\n"
}
} else {
return $a." NOT EXISTS\n";
}
return 0;
}
$filecheck = test("/abc/def.txt");
if($filecheck){
echo $filecheck;
} else {
echo "unknown error";
}
Above shows how to return a value, and do some basic handling.
It is always good to implement return in your functions so you can even specify error code for your functions for reference.
Based on your example, I'll modify slightly like below:
function test($a){
blah;
blahh;
blahhh;
blahhhhh;
return 1;
}
So I know the code is running to last line.
Otherwise the function just finishes silently.
It's useful if you want a function to return a value.
i.e.
Function FavouritePie($who) {
switch($who) {
case 'John':
return 'apple';
case 'Peter':
return 'Rhubarb';
}
}
So, considering the following:
$WhatPie = FavouritePie('John');
echo $WhatPie;
would give
apple
In a really simple form, it's useful if you want a function to return something, i.e. process it and pass something back. Without a return, you'd just be performing a function with a dead end.
Some further reading:
http://php.net/manual/en/function.return.php
http://php.net/manual/en/functions.returning-values.php
To add some further context specific to the answer, if the question boils down to "Do I need to add a return for the sake of it, at the end of a function, then the answer is no. But that doesn't mean the correct answer is always to leave out your return.
it depends what you want to do with $a.
If you echo $a within the function, that function will spit out $a as soon as it is called. If you return $a, assuming you set the calling of test to a variable (i.e. $something = $test('foo')), then you can use it later on.
I'm trying to edit my co-worker's code (he's on vacation and out of reach) and there is this if statement:
if($this->carrier() == 1 and $this->carrier() == 2) {
return 'V';
}
My hope is that he accidentally put "and" instead of "or" which could explain a bug I'm getting, but he knows much more about PHP than I do and I want to be sure that this is wrong instead of just counter intuitive.
Yes, since it's a function with potential side effects, it might be true.
For example:
function carrier() {
return $someValue++;
}
Yes. The carrier() method could increment whatever value it returns each time you call it.
There's a small chance it could, yes.
He's calling a function twice, and you've not included the text of that function. So that could be doing something we can't see, like counting the number of times it's been called by this process.
On the other hand, it's much more likely that it is indeed a typo.
It is possible. Here is a working example you can run.
class program
{
private $i = 1;
function carrier()
{
$this->i=$this->i+1;
return $this->i-1;
}
function run()
{
if ($this->carrier()==1 && $this->carrier()==2)
{
echo "works";
}
else
{
echo "doesnt work" . $i;
}
}
}
$prog = new Program();
$prog->run();
I asked a question yesterday about the USE identifier and it was answered PHP 5.3.0 USE keyword -- how to backport in 5.2?.
However I've had to extend my script to do this twice and unsure how I accommodate both
$available_event_objects = array_filter($event_objects, function ($event_object) use ($week_events) {
// keep if the event is not in $week_events
return !in_array($event_object, $week_events);
});`
and
$calendar_weeks[$week_count][$calendar_date] = array_filter($available_event_objects, function ($event_object) use ($date_pointer) {
// keep if the event is happening on this day
return ($date_pointer >= $event_object->start_date && $date_pointer <= $event_object->end_date);
});`
How can I change this to get it to work in 5.2.9?
Can someone point me in the right direction??
Cheers
PHP did not have anonymous functions before 5.3. You must use any of the callback types instead. Because this gets more difficult and is not very idiomatic with use cases such as yours, I would advise you to apply an imperative programming style instead.
$available_event_objects = array();
foreach ($event_objects as $event_object) {
if (in_array($event_object, $week_events)) {
$available_event_objects[] = $event_object;
}
}
That said, for this case you can freely use array_intersect, ie. $available_event_objects = array_intersect($week_events, $event_objects);
It's execeptionally sketchily covered in the manual http://www.php.net/manual/en/functions.anonymous.php under "closures".
What use ($var) does is share a variable between the anonymous function and the parent scope. Usually it will just keep the initial value, and turn that parameter practically into sort of an static variable.
To turn it into a PHP 5.2 compatible construct, it is always best to turn the closures into static callback functions. Instead of = function () {} write an ordinary declaration:
function cb_event_filter_week($event_object) {
A very non-pretty way would be to share the closure/use variable via the global scope instead. For that rewrite the function to
function cb_event_filter_week($event_object) {
global $week_events;
You will have to do the same in the parent function, also to initialize it! And it is highly advisable to give that variable a significantly more unique name. A nicer alternative here would be a static variable, if you only need to invoke this callback function at one point (!) in the application flow:
function cb_event_filter_week($event_object) {
static $week_events = 0;
Really depends on how it is utilized. But in either case you can then write = array_filter($event_objects, "cb_event_filter_week") for using them in PHP 5.2
Updated answer:
While the answer in the original question is correct, and does allow you to easily use array_filter in php 5.2, without closures; it will be easier to simply do a for loop:
$output = array_filter($input, function($input) use ($stuff) { return /* condition */ } );
Changes to :
$output = array();
foreach($input as $key=>$value) {
if (/* condition */) {
$output[$key] = $value;
}
}
I'm having some problems getting my object to gracefully fail out if an invalid parameter is given during instantiation. I have a feeling it's a small syntax thing somewhere that I simply need fresh eyes on. Any help is more than appreciated.
class bib {
protected $bibid;
public function __construct($new_bibid) {
if(!$this->bibid = $this->validate_bibid($new_bibid)) {
echo 'me';
return false;
}
//carry on with other stuff if valid bibid
}
private static function validate_bibid($test_bibid) {
//call this method every time you get a bibid from the user
if(!is_int($test_bibid)) {
return false;
}
return (int)$test_bibid;
}
}
Note that I have an 'echo me' line in there to demonstrate that it is in fact, returning false. The way that I'm calling this in my PHP is as follows:
if(!$bib=new bib('612e436')) {
echo 'Error Message';
} else {
//do what I intend to do with the object
}
This outputs the me from above, but then continues on into the else block, doing what I intend to do with a valid object.
Can anyone spot anything I'm doing wrong in there?
Thanks!
I see several problems in this code.
First of all, I think you want to do something like this:
$myBib=new bib();
if($myBib->validate_bibid('612e436'))
{ ..do stuff.. }
(or something similar)
remember that __construct is not a normal method. It's a constructor, and it shouldn't return anything. It already implicitly returns a reference to the new instance that you've made.
Second, your validate_bibid returns either a boolean or an integer. You won't get immediate problems with that, but I personally don't like the style.
Third, you've declared a protected member $bibid, but you don't set or reference it anywhere. I'd expect it to be set in the constructor for example. After that you can just call validate_bibid without any argument.
This piece of code is obviously confusing you because it has some weird constructs and therefore doesn't behave in a normal way. I'd suggest to rethink and rewrite this piece from scratch.
Update:
Another issue:
I don't think this line does what you think it does:
if(!$this->bibid = $this->validate_bibid($new_bibid)) {
You probably mean this:
if(!$this->bibid == $this->validate_bibid($new_bibid)) {
// Or even better:
if($this->bibid <> $this->validate_bibid($new_bibid)) {
You can't return in a constructor in PHP - the object is still created as normal.
You could use a factory or something similar;
if(!$bib = Bib_Factory::loadValidBib('612e436')){
echo 'Error Message';
} else {
//do what I intend to do with the object
}
Or throw an exception in the constructor and use a try catch instead of the if statement.
definitely, you need to have comparison with == instead of = which is an assign operation.
Do you consider this a code smell?
foreach((array)$foo as $bar)
{
$bar->doStuff();
}
Should i use that instead?
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
Any other good practices to cover not set variables and assert an array?
They're both code smells. The second one is just evading all the error messages, kind of like turning off the fire alarm before you set your kitchen on fire. Both of those tell you that you have no idea what's in the variable $foo or if it's even been defined in the code above. You need to go back up through the code and find out exactly what's going on with $foo.
If it was my code, $foo would probably be always defined either as an array, or else false to indicate the array isn't needed:
if(do_we_need_an_array())
$foo = function_returning_an_array();
else
$foo = false;
[...snip...]
if($foo)
foreach($foo as $f) { ... }
If you are testing if variables are set, you can initialize them:
if (! $foo or !is_array($foo))
$foo = array();
foreach($foo as $bar)
{
$bar->doStuff();
}
Personally, I would never do the first method and always opt for the second.
If $foo should always be an array, then the second form would be much better if you did some kind of handling for the error case, e.g.:
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
else
{
// This should not happen, exit angrily.
exit("Oh crap, foo isn't an array!");
}
Of course you don't have to just exit the application, but do whatever is appropriate in that case, maybe logging or some alternate logic.
(array)$foo != if (isset($foo) && is_array($foo))
The (array) cast can be useful for casting objects to arrays or scalars to arrays so you can create consistent interfaces to variables that may contain single values or arrays.
(array)$foo == array($foo)
As defined in the PHP Manual for Array Types.
So if you need to always use an array then the first code snippet you presented would be the answer. However type casting rules still apply so you may not get what you want, so look to the manual for more info. Otherwise the second option would prevent accessing unset variables that are not arrays.
As far as a code smell, I would say that checking for unset variables can certainly be avoided, however always knowing that a variable is going to have an array is more often than not, going to creep up. So I would aim to keep code wrapped in is_array($foo) if-then statements to a minimum.
I usually do this to make sure a foreach can handle both scalars and collections:
<?php
foreach (makeSureTraversable($scalarOrCollection) as $val)
{
// Do something.
}
function
makeSureTraversable($anything)
{
if (is_array($anything) || ($anything instanceof Traversable))
{
return $anything;
}
else
{
return array($anything);
}
}
This way I also handle classes that implement Traversable (from the SPL), which means allowing them to be used in foreaches.
if (!isset($foo) && !is_array($foo)) {
throw new InvalidArgumentException('Wrong array passed');
// Or do something to recover lost array
}
foreach($foo as $bar) {
$bar->doStuff();
}
There's quite a few times that you'd like to write a function to take one or more values for a parameter:
function getNamesById($id) { }
In this case, it would make sense that if this function was called with an array of ids, it should probably return an array of names. Similarly, to save the calling code from having to wrap the input in an array and then unwrap the output, if you just pass a scalar, then a scalar should be returned. Consider the likely contents of the function designed to handle both scalar and array parameters:
function getNamesById($id) {
$returnAnArray = is_array($id);
$output = array();
foreach ((array)$id as $theId) {
// perform some logic
$output[] = someFunction($theId);
}
return $returnAnArray ? $output : $output[0];
}
You can see that in this case, casting to an array definitely makes things a lot easier for everyone. As they say, be liberal in what you accept... As long as it is documented that it is expected that a variable could be either, then I see no problem. PHP is a duck-typed language, which has both benefits and drawbacks, but this is one of the benefits, so enjoy it!