Created
December 5, 2011 15:24
-
-
Save ritalin/1433948 to your computer and use it in GitHub Desktop.
Object Instance-time Trait Application.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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