typhoon / overloading
The missing method overloading feature for PHP.
Fund package maintenance!
www.tinkoff.ru/cf/5MqZQas2dk7
Requires
- php: ^8.1
- ext-filter: *
Requires (Dev)
- dragon-code/benchmark: ^2.5
- ergebnis/composer-normalize: ^2.39
- friendsofphp/php-cs-fixer: ^3.38.2
- icanhazstring/composer-unused: ^0.8.10
- infection/infection: ^0.27.8
- maglnet/composer-require-checker: ^4.7.1
- phpunit/phpunit: ^10.4.2
- phpyh/coding-standard: ^2.5.0
- psalm/plugin-phpunit: ^0.18.4
- rector/rector: ^0.18.10
- symfony/filesystem: ^6.3
- symfony/var-dumper: ^6.3.8
- vimeo/psalm: ^5.15.0
README
The missing method overloading feature for PHP.
Installation
composer require typhoon/overloading
Usage
To mark methods handleInt
and handleString
as overloading method handle
,
add #[Overload('handle')]
attribute to handleInt
and handleString
and
call Overload::call()
from handle
. You do not need to pass arguments to Overload::call()
,
this happens automagically. However, return Overload::call()
explicitly if you need to.
After this you will be able to call handle
with any arguments and reach overloading methods
when their signature matches.
use Typhoon\Overloading\Overload; final class WhateverHandler { public function handle(mixed ...$args): string { return Overload::call(); } #[Overload('handle')] public function handleInt(int $int): string { return __METHOD__; } #[Overload('handle')] public function handleString(string $string): string { return __METHOD__; } #[Overload('handle')] public function handleStdClass(\stdClass $object): string { return __METHOD__; } #[Overload('handle')] public function handleNamedOptionalArguments(int $int = 0, float $float = M_E): string { return __METHOD__; } } $handler = new WhateverHandler(); // WhateverHandler::handleInt var_dump($handler->handle(300)); // WhateverHandler::handleString var_dump($handler->handle('Hello world!')); // WhateverHandler::handleStdClass var_dump($handler->handle(new \stdClass())); // WhateverHandler::handleNamedOptionalArguments var_dump($handler->handle(float: 1.5)); // WhateverHandler::handleNamedOptionalArguments var_dump($handler->handle()); // No matching overloading methods for WhateverHandler::handle(string, bool). var_dump($handler->handle('Hey!', true));
What about speed?
Well, using overloading is obviously slower, than calling a method directly, but not awfully slower.
Here's a simple benchmark for the WhateverHandler
:
// warm up $handler->handle(); \DragonCode\Benchmark\Benchmark::start() ->withoutData() ->round(2) ->compare([ 'direct call' => static fn (): string => $handler->handleNamedOptionalArguments(), 'overloaded call' => static fn (): string => $handler->handle(), ]);
------- ---------------- -------------------
# direct call overloaded call
------- ---------------- -------------------
min 0 ms - 0 bytes 0 ms - 0 bytes
max 0 ms - 0 bytes 0.02 ms - 0 bytes
avg 0 ms - 0 bytes 0 ms - 0 bytes
total 0.95 ms 1.16 ms
------- ---------------- -------------------
Order - 1 - - 2 -
------- ---------------- -------------------
It's important to understand that memoization plays a very important role here. CLI workers and applications, served
via Roadrunner, for instance, will benefit from this. For PHP-FPM you can enable file cache suitable for OPcaching via
Overload::useFileCache('/path/to/cache');
.
TODO
- Finish tests.
- Explain caching in README.
- Optimize generated code.
- Inherit attributes from upstream method declarations.
- Allow to warm up classes.
- Psalm plugin.
- PHPStan plugin.
- Support static analysis types.