php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71980 Decorated/Nested Generator is Uncloseable in Finally
Submitted: 2016-04-06 19:30 UTC Modified: 2016-04-07 06:13 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: php at fleshgrinder dot com Assigned:
Status: Closed Package: Scripting Engine problem
PHP Version: Irrelevant OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: php at fleshgrinder dot com
New email:
PHP Version: OS:

 

 [2016-04-06 19:30 UTC] php at fleshgrinder dot com
Description:
------------
The finally block of a Generator that is decorated with or nested in another Generator is never called. This behavior is of course true for multiple decoration or nesting levels.

Using a finally block for clean-up in case a Generator is aborted is the official and only way of doing so. It is also recommended in the initial RFC:

https://wiki.php.net/rfc/generators#closing_a_generator

Please see attached test script as well as expected and actual result.

Test script:
---------------
#!/usr/bin/env php
<?php

function generator() {
	try {
		while (true) {
			yield 'Hello';
		}
	}
	finally {
		echo "Generator Clean-up\n";
	}
}

function generator_decoration() {
	try {
		foreach (generator() as $word) {
			yield $word . " World!\n";
		}
	}
	finally {
		echo "Decorator Clean-up\n";
	}
}

foreach (generator_decoration() as $sentence) {
	echo $sentence . "Break\n";
	break;
}

echo "Shutdown\n";


Expected result:
----------------
Hello World!
Break
Decorator Clean-up
Generator Clean-up
Shutdown

Actual result:
--------------
Hello World!
Break
Decorator Clean-up
Shutdown
Generator Clean-up

Workaround:
----------------

In case a frustrated user is finding this bug report. There is a possible workaround that works in all PHP versions that support Generators via a signal that is being sent to the Generator. Please have a look at the following example that makes use of it.

#!/usr/bin/env php
<?php

const GENERATOR_BREAK = 'PHP_GENERATOR_BREAK';

function generator() {
	try {
		while (true) {
			if ((yield 'Hello') === GENERATOR_BREAK) {
				break;
			}
		}
	}
	finally {
		echo "Generator Clean-up\n";
	}
}

function generator_decoration() {
	$generator = generator();
	try {
		foreach ($generator as $word) {
			yield $word . " World!\n";
		}
	}
	finally {
		echo "Decorator Clean-up\n";
		$generator->send(GENERATOR_BREAK);
	}
}

foreach (generator_decoration() as $sentence) {
	echo $sentence . "Break\n";
	break;
}

echo "Shutdown\n";


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-04-07 06:13 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2016-04-07 06:13 UTC] nikic@php.net
We're leaking the inner generator here. We need to perform partial unfinished execution cleanup before jumping to the finally block.
 [2016-04-07 10:30 UTC] nikic@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=aad4ecebf83568f41f5b27b274f988710ce53646
Log: Fixed bug #71980
 [2016-04-07 10:30 UTC] nikic@php.net
-Status: Verified +Status: Closed
 [2016-04-07 17:11 UTC] php at fleshgrinder dot com
Awesome, thanks for this very fast fix.
 [2016-07-20 11:32 UTC] davey@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=aad4ecebf83568f41f5b27b274f988710ce53646
Log: Fixed bug #71980
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Tue Feb 04 05:01:30 2025 UTC