php[tek] 2018 : Call for Speakers

goto

(PHP 5 >= 5.3.0, PHP 7)

¿Qué es lo peor que podría suceder si utiliza goto?
Imagen cortesía de » xkcd

El operador goto puede ser usado para saltar a otra sección en el programa. El punto de destino es especificado mediante una etiqueta seguida de dos puntos y la instrucción es dada como goto seguida de la etiqueta del destino deseado. Este goto no es completamente sin restricciones. La etiqueta de destino debe estar dentro del mismo fichero y contexto, lo que significa que no se puede saltar fuera de una función o método, ni se puede saltar dentro de uno. Tampoco se puede saltar dentro de cualquier clase de estructura de bucle o switch. Se puede saltar fuera de estos y un uso común es utilizar un goto en lugar de un break multi-nivel.

Ejemplo #1 Ejemplo de goto

<?php
goto a;
echo 
'Foo';
 
a:
echo 
'Bar';
?>

El resultado del ejemplo sería:

Bar

Ejemplo #2 Ejemplo de goto en un bucle

<?php
for($i=0,$j=50$i<100$i++) {
  while(
$j--) {
    if(
$j==17) goto end;
  }  
}
echo 
"i = $i";
end:
echo 
'j alcanzó 17';
?>

El resultado del ejemplo sería:

j alcanzó 17

Ejemplo #3 Esto no funcionará

<?php
goto loop;
for(
$i=0,$j=50$i<100$i++) {
  while(
$j--) {
    
loop:
  }
}
echo 
"$i = $i";
?>

El resultado del ejemplo sería:

Fatal error: 'goto' into loop or switch statement is disallowed in script on line 2
(Error fatal: 'goto' hacia el interior de un bucle o sentencia switch no esta permitido en el script en la línea 2)

Nota:

El operador goto está disponible a partir de PHP 5.3.

add a note add a note

User Contributed Notes 11 notes

up
68
chrisstocktonaz at gmail dot com
8 years ago
Remember if you are not a fan of wild labels hanging around you are free to use braces in this construct creating a slightly cleaner look. Labels also are always executed and do not need to be called to have their associated code block ran. A purposeless example is below.

<?php

$headers
= Array('subject', 'bcc', 'to', 'cc', 'date', 'sender');
$position = 0;

hIterator: {

   
$c = 0;
    echo
$headers[$position] . PHP_EOL;

   
cIterator: {
        echo
' ' . $headers[$position][$c] . PHP_EOL;

        if(!isset(
$headers[$position][++$c])) {
            goto
cIteratorExit;
        }
        goto
cIterator;
    }

   
cIteratorExit: {
        if(isset(
$headers[++$position])) {
            goto
hIterator;
        }
    }
}
?>
up
43
Ray dot Paseur at Gmail dot com
6 years ago
You cannot implement a Fortran-style "computed GOTO" in PHP because the label cannot be a variable. See: http://en.wikipedia.org/wiki/Considered_harmful

<?php // RAY_goto.php
error_reporting(E_ALL);

// DEMONSTRATE THAT THE GOTO LABEL IS CASE-SENSITIVE

goto a;
echo
'Foo';
a: echo 'Bar';

goto
A;
echo
'Foo';
A: echo 'Baz';

// CAN THE GOTO LABEL BE A VARIABLE?

$a = 'abc';
goto
$a; // NOPE: PARSE ERROR
echo 'Foo';
abc: echo 'Boom';
?>
up
21
D. Kellner
2 years ago
However hated, goto is useful. When we say "useful" we don't mean "it should be used all the time" but that there are certain situations when it comes in handy.

There are times when you need a logical structure like this:
<?php
// ...
do {

   
$answer = checkFirstSource();
    if(
seemsGood($answer)) break;

   
$answer = readFromAnotherSource();
    if(
seemsGood($answer)) break;

   
// ...

}while(0);
$answer = applyFinalTouches($answer);
return
$answer;
?>

In this case, you certainly implemented a goto with a "fake loop pattern".  It could be a lot more readable with a goto; unless, of course, you hate it.  But the logic is clear: try everything you can to get $answer, and whenever it seems good (e.g. not empty), jump happily to the point where you format it and give it back to the caller.  It's a proper implementation of a simple fallback mechanism.

Basically, the fight against goto is just a side effect of a misleading article many decades ago.  Those monsters are gone now.  Feel free to use it when you know what you're doing.
up
5
R. Borchmann
9 months ago
If you feel the urge to leave a nested loop with goto, better think again. Probably you've got a piece of your code that should be refactored into a function.

Instead of

<?php
  
for ($i=0; $i<10; $i++) {
      for (
$j=$i, $j<11; $j++) {
         if (
$data[$i] === $data[$j] )
             goto
foundit;
      }
   }
   echo
"Sorry, no match";
   goto
nextAction;
foundit:
   echo
"Duplicate at $i and $j)";
nextAction:
?>

you better write

<?php
list($success, $i, $j) = searchForDuplicate( $data );

if (
$success)
   echo
"Found it at ($i, $j)";
else
   echo
"Sorry, no match";

function
searchForDuplicate( &$data )
{
   for (
$i=0; $i<10; $i++) {
      for (
$j=$i, $j<11; $j++) {
         if (
$data[$i] === $data[$j] )
             return [
true, $i, $j ];
      }
   }
   return [
false];
}
?>

or return [$i, $j] and [-1, -1] if you don't like an extra $success variable. Refactoring into a function is cleaner and gives you an auto-documentation about what the loop is doing.
up
14
f at francislacroix dot info
5 years ago
The goto operator CAN be evaluated with eval, provided the label is in the eval'd code:

<?php
a
: eval("goto a;"); // undefined label 'a'
eval("a: goto a;"); // works
?>

It's because PHP does not consider the eval'd code, containing the label, to be in the same "file" as the goto statement.
up
14
sixoclockish at gmail dot com
5 years ago
You are also allowed to jump backwards with a goto statement. To run a block of goto as one block is as follows:
example has a prefix of iw_ to keep label groups structured and an extra underscore to do a backwards goto.

Note the `iw_end_gt` to get out of the labels area

<?php
    $link
= true;

    if (
$link ) goto iw_link_begin;
    if(
false) iw__link_begin:
   
    if (
$link ) goto iw_link_text;
    if(
false) iw__link_text:
   
    if (
$link ) goto iw_link_end;
    if(
false) iw__link_end:
   
    goto
iw_end_gt;
   
   
    if (
false) iw_link_begin:
        echo
'<a href="#">';
    goto
iw__link_begin;
   
    if (
false) iw_link_text:
        echo
'Sample Text';
    goto
iw__link_text;
   
    if (
false) iw_link_end:
        echo
'</a>';
    goto
iw__link_end;
   
   
iw_end_gt:
?>
up
1
Mark
2 months ago
Here are two examples of how GOTO can simplify code and actually make it more readable and easier to understand.

1. GOTO can be used to exit an IF block:

<?php
   
if(something) {
        if(
nothing) goto endif;
       
//    blah blah
        //    blah blah
       
       
endif:
           
// Nothing here, but maybe some cleanup
   
}   
?>

2. GOTO can be used to jump ahead to a section of code. This can be useful for trouble shooting or testing a script in small stages.

<?php
   
   
goto block3;
   
   
block1:
       
//    blah blah
   
   
block2:
       
//    blah blah
       
   
block3:
       
//    blah blah
?>

Note that in both cases, I have gone forward, not backward. This makes it more manageable.

In the case of the IF block, many structures, such as a loop, a switch or a function, have an early exit, such as break or return. IF doesn’t, so this is a workable solution
up
-1
at YearOfCodes (retrobytespr at mail dot com)
4 months ago
Here is an example of re-using labels in separate methods - in this example, the end: label is used in each, with the goto condition behaving like an if/else condition:

class DateController
{
    public $day, $month, $year;
    public function __construct(){
        $this->day   = $this->setDays();
        $this->month = $this->setMonths();
        $this->year  = $this->setYears(1901, (int)date('Y'), 'asc');
    }
   
    /**
     * @param    int
     * @return   array
     */
    protected function setDays(int $default = 0){
        $days    = array();
        for($i = 1; $i <= 31; $i++){
            $day    = "{$i}";
            if($i<10){
                $day    = "0{$i}";
            }
            $days[$day]    = $day;
        }
        if($default == 0){
            goto end;
        }
        $days['default']    = $default;
       
        end:
        return $days;
    }
   
    /**
     * @param    string, string, string
     * @return   array
     */
    protected function setMonths(string $type = "full", string $keyType = "numeric", string $default = ''){
        $keys = array(
            'numeric' => array(
                "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"
            ),
            'full'    => array(
                "January", "February", "March", "April", "May", "June", "July",
                "August", "September", "October", "November", "December"
            ),
            'short'   => array(
                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
            )
        );
        $months   = array();
        $index   = 0;
        foreach($keys[$keyType] as $primaryKey){
            $months[$primaryKey]  = $keys[$type][$index];
            $index++;
        }
        if($default = ''){
            goto end;
        }
        $months['default'] = $default;
       
        end:
        return $months;
    }
   
    /**
     * @param    int, int, string
     * @return   array
     */
    protected function setYears(int $start = 1977, int $end = 2017, $order = "asc"){
        $years    = array();
        if($order == "asc"){
            for($i = $start; $i <= $end; $i++){
                $years["{$i}"]  = $i;
            }
            goto end;
        }
        for($i = $end; $i >= $start; $i--){
            $years["{$i}"]  = $i;
        }
       
        end:
        return $years;
    }
}
up
-7
kle at lekhoi dot com
1 year ago
Goto can also go into an infinite loop as the example below.

<?php

goto start;

start: echo 'start';

working: {
    echo
'working';
    ...
    goto
start;
    echo
'never executed';
}
?>

Output
startworkingstartworking ...
up
-12
ivan dot tc at gmail dot com
2 years ago
This works good:

<?php
goto start;

five:
echo
$i;
goto
end;

start:
echo
'I have ';

for (
$i=0; $i < 10; $i++) {
  if (
$i == 5) {
    goto
five;
  }
}

end:
echo
' apples';
?>

Output: I have 5 apples.

This don't work:

<?php
goto start;

five:
echo
$i;
goto
end;

start:
echo
'I have ';
$count();

end:
echo
' apples';

$count = function () {
  for (
$i=0; $i < 10; $i++) {
    if (
$i == 5) {
      goto
five; // line 18
   
}
  }
}
?>

PHP Fatal error:  'goto' to undefined label 'five' on line 18
up
-43
ivan dot sammartino at gmail dot com
2 years ago
I found it useful for switch statements:

<?php
$action
= $_GET['action'];
switch (
$action){
    case(
'a'):
       
mylabel: {
           
doStuff();
            break;
        }
    case(
'b'):
        if (
true){
           
doAnotherStuff();
        } else {
            goto
mylabel;
        }
        break;
}
?>
To Top