Skip to content

Instantly share code, notes, and snippets.

@SammyK
Last active April 3, 2018 05:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SammyK/2710a3d3ffe975025260b6e07c3375f6 to your computer and use it in GitHub Desktop.
Save SammyK/2710a3d3ffe975025260b6e07c3375f6 to your computer and use it in GitHub Desktop.
Possible syntax for adding the `retry` keyword to PHP 7.next

Without retry keyword

try {
	$attempts = 0;
	retry:
	throw new Exception('Oops!');
} catch (Exception $e) {
	if ($attempts < 4) {
		$attempts++;
		echo 'Fail - trying again...'.PHP_EOL;
		sleep(1);
		goto retry;
	}
	echo $e->getMessage().PHP_EOL;
}

Proposed retry keyword

The retry keyword can be used in a catch block to optionally execute an abritraty statement before jumping to the top of the try block.

Retry n times & execute code block before trying

# Litteral number example
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry 4 {
		echo 'Fail - trying again...'.PHP_EOL;
		sleep(1);
	}
	echo $e->getMessage().PHP_EOL;
}
# Constant examples
const RETRY_TIMES = 4;
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry RETRY_TIMES { sleep(1); }
	echo $e->getMessage().PHP_EOL;
}

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry \Foo\RETRY_TIMES { sleep(1); }
	echo $e->getMessage().PHP_EOL;
}

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry \Foo\Bar::RETRY_TIMES { sleep(1); }
	echo $e->getMessage().PHP_EOL;
}

Retry n times with no code block execution

# Litteral number example
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry 4;
	echo $e->getMessage().PHP_EOL;
}
# Constant examples
const RETRY_TIMES = 4;
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry RETRY_TIMES;
	echo $e->getMessage().PHP_EOL;
}

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry \Foo\RETRY_TIMES;
	echo $e->getMessage().PHP_EOL;
}

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry \Foo\Bar::RETRY_TIMES;
	echo $e->getMessage().PHP_EOL;
}

Examples of trying forever

Possibly useful in async contexts?

Retry forever and execute code block

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry {
		echo 'Fail - trying again...'.PHP_EOL;
		sleep(1);
	}
}
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry INF {
		echo 'Fail - trying again...'.PHP_EOL;
		sleep(1);
	}
}

Retry forever with no code block

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry;
}

Originally proposed syntax that is not possible

Removed: It is impossible to disambiguate a expr and statement at the parser level, so the following syntax examples are not possible. The statements must always be wrapped with {}.

try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry 4 sleep(1);
	echo $e->getMessage().PHP_EOL;
}
try {
	throw new Exception('Oops!');
} catch (Exception $e) {
	retry sleep(1);
}
@Jean85
Copy link

Jean85 commented Apr 10, 2017

I'm sensing some discrepancy in using a keyword in such a different way as catch. Couldn't we do something like this?

try {
    throw SomeException();
} retry 3 (SomeException $e, $currentAttemp) {
    echo 'Attempt number ' . $currentAttempt;
    sleep(1);
} catch (SomeException $e) {
    echo 'Failed even after 3 attempts';
    throw $e;
} catch (OtherException) {
    // ...
} 

The second argument would be optional, and I would also accept these cases:

} retry $maxAttempts (SomeException $e, $currentAttemp) {
} retry self::SOME_COSTANT (SomeException $e, $currentAttemp) {
} retry (SomeException $e, $currentAttemp) { // infinite retries

In this way it seems more coherent and elegant. What do you think?

@amochohan
Copy link

amochohan commented Apr 11, 2017

I like the following syntax:

try ($attempts = 1, $wait = 0) {
  // Try $attempts times to do something, waiting $wait milliseconds between trys 
  // Within the try block scope, we have access to the $attempts and $wait arguments
  $this->log('Doing something important attempt: ' . $attempt);
}
catch (Exception $e) {
  // Do something
}

And also (possibly easier to implement and prevent b/c issues?)

try ($attempts = 3, $wait = 500) {
    // Try $attempts times to do something, waiting $wait milliseconds between trys 
}
retry ($attemptNumber) {
    // Calls the try block again, but could also be used for some sort of logging
    $this->log('Failed, so retrying on attempt: ' . $attempt);
}
catch (Exception $e) {
  // Do something
}

Having a specific retry keyword may impose breaking code issues for projects, so I still think I prefer my first solution which should be possible to implement without breaking b/c (I think!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment