interitty / output-buffer-manager
Output buffer manager brings a safe way to work with outputs.
Requires
- php: ~8.3
- dg/composer-cleaner: ~2.2
- interitty/di: ~1.0
- interitty/utils: ~1.0
Requires (Dev)
- interitty/application: ~1.0
- interitty/code-checker: ~1.0
- interitty/phpunit: ~1.0
README
Output buffer manager brings a safe way to work with outputs.
Requirements
- PHP >= 8.3
Installation
The best way to install interitty/output-buffer-manager is using Composer:
composer require interitty/output-buffer-manager
Then register the extension in the Nette config file:
# app/config/config.neon
extensions:
outputBufferManager: Interitty\OutputBufferManager\Nette\DI\OutputBufferManagerExtension
Features
For more complex applications where the work with the output buffer
is needed, this package helps to organize individual handlers.
The naming of each handler is requested, because it helps to prevent very difficult-to-detect and unnecessary problems.
It also allows for an easy route to multiple concurrent handlers at the same moment.
Output buffer manager does nothing until requested. Because of that, it can be registered as a service in the
dependency injection container
or service locator and be used wherever it is needed.
In the following examples, the new instance of OutputBufferManager
class is created for simplification. The manager
works with the so-called "handlers", which are anonymous functions
or callbacks.
These "handlers" are encapsulated into objects of class Handler
, that represent simple implementation
of the HandlerInterface
, which is used internally.
This mechanism provides additional events, that can be useful in some cases.
Example: catch content into a variable
The following example catches any content that was sent to the output and appends it to the given $output
variable.
It also shows, that "handler" can be defined as an anonymous functions
Because the handler doesn't have a return value, nothing will be sent to the output or another registered handler.
$output = null;
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', function(string $bufferOutput) use(&$output): string {
$output .= $bufferOutput;
return '';
});
// Any content that was sent to the output
echo 'testContent';
$outputBufferManager->end('…');
Example: catch content into property
The following example catches any content that was sent to the output and appends it into the Example::$output
property.
It also shows, that "handler" can be defined as a callback.
class Example
{
public string $output = '';
public function __construct(): void
{
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', [$this, 'handleCatchContent']);
// Any content that was sent to the output
echo 'testContent';
$outputBufferManager->end('…');
}
public function handleCatchContent(string $bufferOutput): string
{
$this->output .= $bufferOutput;
return '';
}
}
Example: proxy content into a variable
The following example proxy any content that was sent to the output and appends it into the given $output
variable.
Because the handler has the return value, everything will be also sent to the output or another registered handler.
$output = null;
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(string $bufferOutput) use(&$output): string {
$output .= $bufferOutput;
return $bufferOutput;
});
// Any content that was sent to the output
echo 'testContent';
$outputBufferManager->end('…');
Example: flush content of output buffer
In some cases, it could be useful to manually flush the content of the output buffer into handlers.
For that purpose, there is a flush
method on the OutputBufferManager
object.
As with other methods, it also expects the name of the current handler to prevent ugly problems.
$outputBufferManager = new OutputBufferManager();
// in some cases like in the foreach cycle …
$outputBufferManager->flush('…');
$outputBufferManager->end('…');
When things go wrong
In some cases is possible to see the following exceptions.
LogicException: Output buffer handler "…" is already registered
As the message of the exception says, another handler with the same name was registered before. The core of the problem is similar to the following code.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(): string {return '';});
$outputBufferManager->begin('…', static function(): string {return '';});
There can be just one handler of the same name at the same time.
Solution
The solution is to use a different (unique) name or stop using the last one and begin work with the new one.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(): string {return '';});
// warning: calling end flush unflushed data
$outputBufferManager->end('…');
$outputBufferManager->begin('…', static function(): string {return '';});
LogicException: Current output buffer manager is not OutputBufferManager
The core of the problem is, that OutputBufferManager
is not the current Output buffer handler.
It can happen when OutputBufferManager
was not started yet.
$outputBufferManager = new OutputBufferManager();
// Forgotten call of $outputBufferManager->begin('…', static function(): string {return '';});
$outputBufferManager->end('…');
Another possibility is that there is a registered different handler than OutputBufferManager
.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(): string {return '';});
// Maybe the old mechanism of the Output buffer was forgotten in the code
ob_start(static function(): string {return '';});
$outputBufferManager->end('…');
Solution
The possible solution depends on the use case:
- Replace old handlers with the
OutputBufferManager
. - Flush the old handler (
ob_end_clean
orob_end_flush
) to fall back intoOutputBufferManager
. - Register new
OutputBufferManager
viaOutputBufferManager::begin
.
LogicException: Expected output buffer handler "…" is not registered
The core of the problem is mostly in the misspelled name of the handler.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('...', static function(): string {return '';});
$outputBufferManager->end('…');
Solution
Fix the name of the expected handler. It is better to use constants for handler names to prevent this problem.
define('OUTPUT_BUFFER_MAIN', '…');
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin(OUTPUT_BUFFER_MAIN, static function(): string {return '';});
$outputBufferManager->end(OUTPUT_BUFFER_MAIN);
LogicException: Current output buffer handler "..." is not expected "…"
The core of the problem is mostly in the forgotten termination of the last used handler or crossing the call.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(): string {return '';});
$outputBufferManager->begin('...', static function(): string {return '';});
// Somebody forgot call $outputBufferManager->end('...');
$outputBufferManager->end('…');
Solution
Add forgotten termination of the previous handler or correct the position of the handler calling.
$outputBufferManager = new OutputBufferManager();
$outputBufferManager->begin('…', static function(): string {return '';});
$outputBufferManager->end('…');
$outputBufferManager->begin('...', static function(): string {return '';});
$outputBufferManager->end('...');