Skip to content

Instantly share code, notes, and snippets.

@ritalin
Created December 5, 2011 15:24
Show Gist options
  • Save ritalin/1433948 to your computer and use it in GitHub Desktop.
Save ritalin/1433948 to your computer and use it in GitHub Desktop.
Object Instance-time Trait Application.
<?php
use Symfony\Component\ClassLoader\UniversalClassLoader;
class TraitExt {
public static $clsLoader;
public static function withTraits($class, array $traits) {
$cache = self::classLoader()->getNamespaceFallbacks();
if (empty($cache)) {
self::registerCacheDir($_SERVER["TMPDIR"] . DIRECTORY_SEPARATOR . "trait_ext_cache");
}
if (! class_exists($class, true)) {
// 例外を投げる
throw new InvalidArgumentException("未定義クラス({$class})");
}
$refClass = new ReflectionClass($class);
if (empty($traits)) return new TraitExtActivator($refClass);
// traitsの確認
$without = array_diff($traits, $refClass->getTraitNames());
if (empty($without)) {
return new TraitExtActivator($refClass);
}
// クラス名の決定
$traitWithclass = $class . implode("", array_map(
function ($v) {
if (false !== ($p = strrpos($v, '\\'))) $v = substr($v, $p+1);
return $v;
},
$without
));
// キャッシュからの探索
if (self::classLoader()->findFile($traitWithclass) === null) {
// trait拡張クラスをエクスポートする
self::export($traitWithclass, $class, $without);
}
return new TraitExtActivator(new ReflectionClass($traitWithclass));
}
public static function classLoader(UniversalClassLoader $loaderClass = null) {
if (self::$clsLoader === null) {
if ($loaderClass !== null) {
self::$clsLoader = new $loaderClass();
}
else {
self::$clsLoader = new UniversalClassLoader();
}
self::$clsLoader->register();
}
return self::$clsLoader;
}
public static function registerCacheDir($dir) {
self::classLoader()->registerNamespaceFallbacks(array($dir));
self::classLoader()->registerPrefixFallbacks(array($dir));
}
private static function export($className, $parentClass, array $traits) {
if (preg_match('/(?:\\\\)?(.*)\\\\(.+)/', $className, $m) === 1) {
array_shift($m);
if (count($m) === 1) {
$namespace = "";
$shortClass = $m[0];
}
else {
list($namespace, $shortClass) = $m;
}
}
$ns = $namespace !== "" ? "namespace {$namespace}" : "";
$tr = implode(",", $traits);
$code = <<< WITH_TRAITS
<?php
{$ns};
class {$shortClass} extends {$parentClass} {
use {$tr};
}
WITH_TRAITS;
$dir = sprintf("%s%s%s",
implode("", self::classLoader()->getNamespaceFallbacks()), DIRECTORY_SEPARATOR,
str_replace('\\', DIRECTORY_SEPARATOR, $namespace)
);
$filename = $dir . DIRECTORY_SEPARATOR . sprintf("%s.php", $shortClass);
if (! file_exists($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($filename, $code);
}
}
class TraitExtActivator {
private $className;
public function __construct(ReflectionClass $classRef) {
$this->classRef = $classRef;
}
public function newInstance() {
$args = func_get_args();
if (empty($args)) {
return $this->classRef->newInstanceArgs();
}
else {
return $this->classRef->newInstanceArgs($args);
}
}
public function getClassName() {
return sprintf('\%s', $this->classRef->getName());
}
}
<?php
namespace My;
require_once __DIR__ . "/../lib/Symfony/Component/ClassLoader/UniversalClassLoader.php";
require_once __DIR__ . "/TraitExt.php";
define ("CACHE_DIR", __DIR__ . "/cache");
\TraitExt::registerCacheDir(CACHE_DIR);
class Animal {
public function getKind() {
return "UMA";
}
// メソッドの宣言
public function cry() {
return "...";
}
public function greeting() {
return sprintf("%s : %s\n", $this->getKind(), $this->cry());
}
}
$animal = new Animal();
echo $animal->greeting();
trait Cat {
function getKind() {
return "ぬこ";
}
function Cry() {
return "我が輩は猫であった";
}
}
$c = \TraitExt::withTraits("\My\Animal", ["\My\Cat"]);
$animal = $c->newInstance();
echo $animal->greeting();
trait Dog {
function getKind() {
return "いぬ";
}
function Cry() {
return "今日も平和。わんわんお";
}
}
$animal = \TraitExt::withTraits($c->getClassName(), ["\My\Dog"])->newInstance();
echo $animal->greeting();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment