goto

(PHP 5 >= 5.3.0)

Что наихуджее может случиться, если используется goto ?
Изображение предоставлено » xkcd

Оператор goto используется для перехода в другую часть программы. Место, куда необходимо перейти указывается с помощью метки, за которой ставится двоеточие, после оператора goto указывается желаемая метка для перехода. Оператор не является неограниченным "goto". Целевая метка должна находиться в том же файле, в том же контексте. Имеется ввиду, что вы не можете ни перейти за границы функции или метода, ни перейти внутрь одной из них. Вы также не можете перейти внутрь любой циклической структуры или оператора switch. Но вы можете выйти из них, и обычным применением оператора goto является использование его вместо многоуровневых break.

Пример #1 Пример использования goto

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

Результат выполнения данного примера:

Bar

Пример #2 Пример использования goto в цикле

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

Результат выполнения данного примера:

j hit 17

Пример #3 Следующий пример работать не будет

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

Результат выполнения данного примера:

Fatal error: 'goto' into loop or switch statement is disallowed in
script on line 2

Замечание:

Оператор goto доступен в PHP начиная с версии 5.3.

Коментарии

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;
        }
    }
}
?>
2009-08-07 18:03:50
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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';
?>
2011-10-27 09:59:03
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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.
2011-11-30 15:11:48
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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(
falseiw__link_begin:
   
    if ( 
$link goto iw_link_text;
    if(
falseiw__link_text:
   
    if ( 
$link goto iw_link_end;
    if(
falseiw__link_end:
   
   
goto iw_end_gt;
   
   
    if (
falseiw_link_begin:
        echo 
'<a href="#">';
   
goto iw__link_begin;
   
    if (
falseiw_link_text:
        echo 
'Sample Text';
   
goto iw__link_text;
   
    if (
falseiw_link_end:
        echo 
'</a>';
   
goto iw__link_end;
   
   
iw_end_gt:
?>
2012-06-14 10:58:10
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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;
}
?>
2015-10-13 14:38:20
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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
2015-10-23 05:10:34
http://php5.kiev.ua/manual/ru/control-structures.goto.html
Автор:
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.
2015-10-31 16:12:02
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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 ...
2016-06-24 07:37:08
http://php5.kiev.ua/manual/ru/control-structures.goto.html
Автор:
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.
2017-02-17 10:24:48
http://php5.kiev.ua/manual/ru/control-structures.goto.html
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;
    }
}
2017-07-06 13:05:18
http://php5.kiev.ua/manual/ru/control-structures.goto.html

    Поддержать сайт на родительском проекте КГБ