Skip to content

Instantly share code, notes, and snippets.

@dragon-fish
Created June 13, 2025 04:13
Show Gist options
  • Select an option

  • Save dragon-fish/e8d87cd01a3c5d14fab08bc365e8d38b to your computer and use it in GitHub Desktop.

Select an option

Save dragon-fish/e8d87cd01a3c5d14fab08bc365e8d38b to your computer and use it in GitHub Desktop.
PHP class decorator

PHP class decorator

假设我们现在要给 BaseClass 添加更多的功能:

/**
 * @mixin BaseClass
 * @extends parent<BaseClass>
 * ↑ 添加这些 PHPDoc 注释会很有帮助,它们让 IDE 给出正确的提示
 */
class MyClassPlus extends BaseDecorator {
  // 务必手动调用一下这个方法,以确保静态方法被代理
  public static function __callStatic($method, $args) {
    return self::callStaticOn(BaseClass::class, $method, ...$args);
  }

  // 然后就可以在这里添加你需要的实例方法了
  public function someMethod() {
    // do something
  }
}

现在,你可以快乐地在任意位置使用:

$polyfill = new MyClassPlus($instance);
$polyfill->someMethod();
<?php
declare(strict_types=1);
/**
* @template T of object
* @mixin T
*/
class BaseDecorator {
/** @var T */
private object $instance;
/** @param T $instance */
public function __construct(object $instance) {
$this->instance = $instance;
}
/** @return mixed */
public function __call(string $name, array $arguments): mixed {
if (is_callable([$this->instance, $name])) {
// callable 检查可以排除 protected/private 方法造成的错误
return $this->instance->{$name}(...$arguments);
}
throw new BadMethodCallException(
sprintf('Method %s::%s() is not accessible or does not exist.', $this->instance::class, $name)
);
}
/**
* 静态方法调用工厂
* @template C of object
* @param class-string<C> $class
* @param string $method
* @param mixed ...$args
* @return mixed
*/
public static function callStaticOn(string $class, string $method, mixed ...$args): mixed {
if (is_callable([$class, $method])) {
return $class::$method(...$args);
}
throw new BadMethodCallException(
sprintf('Static method %s::%s() is not accessible or does not exist.', $class, $method)
);
}
/** @return mixed */
public function __get(string $name): mixed {
return $this->instance->$name;
}
public function __isset(string $name): bool {
return isset($this->instance->$name);
}
/** @return T */
protected function getRealInstance(): object {
return $this->instance;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment