好吧,这是一种笨拙的方法。 (使用 eval - gaaaahhh!)
<?php
require_once("../../vendor/autoload.php");
const TRAITS_NAMESPACE = "OurCompany\\ProductBuilder\\Builder\\Traits";
const CLASS_NAMESPACE = "OurCompany\\ProductBuilder\\Builder\\Components";
if(!function_exists('polymorphic_class')) {
function polymorphic_class(array $behaviors)
{
$classBody = "";
$classNameParts = [];
$behaviorUse = sprintf("use %s\\ComponentAbstract;\n", CLASS_NAMESPACE);
$behaviorInit = "";
$behaviorCode = "";
sort($behaviors, SORT_STRING);
foreach($behaviors as $behavior) {
switch($behavior) {
case "Price":
array_push($classNameParts, $behavior);
$behaviorUse .= sprintf("use %s\\Has%s;\n", TRAITS_NAMESPACE, $behavior);
$behaviorInit .= sprintf(" \$this->__has%sInit();\n", $behavior);
$behaviorCode .= sprintf("\n use has%s;\n", $behavior);
$behaviorCode .= <<<EOT
public function setProductPrice(float \$price) { return \$this->setPrice(null, \$price); }
public function getProductPrice() { return \$this->getPrice(); }
EOT;
break;
case "Size":
array_push($classNameParts, $behavior);
$behaviorUse .= sprintf("use %s\\Has%s;\n", TRAITS_NAMESPACE, $behavior);
$behaviorInit .= sprintf(" \$this->__has%sInit();\n", $behavior);
$behaviorCode .= sprintf("\n use has%s;\n", $behavior);
$behaviorCode .= <<<EOT
public function setProductWidth(float \$width) { return \$this->setWidth(null, \$width); }
public function getProductWidth() { return \$this->getWidth(); }
public function setProductLength(float \$length) { return \$this->setLength(null, \$length); }
public function getProductLength() { return \$this->getLength(); }
public function setProductHeight(float \$height) { return \$this->setHeight(null, \$height); }
public function getProductHeight() { return \$this->getHeight(); }
EOT;
break;
case "Weight":
array_push($classNameParts, $behavior);
$behaviorUse .= sprintf("use %s\\Has%s;\n", TRAITS_NAMESPACE, $behavior);
$behaviorInit .= sprintf(" \$this->__has%sInit();\n", $behavior);
$behaviorCode .= sprintf("\n use has%s;\n", $behavior);
$behaviorCode .= <<<EOT
public function setProductWeight(float \$weight) { return \$this->setWeight(null, \$weight); }
public function getProductWeight() { return \$this->getWeight(); }
EOT;
break;
default:
error_log(sprintf("Unknown behavior: '%s'", $behavior));
break;
}
}
if(count($classNameParts) > 0) {
$className = "Generic" . implode("", $classNameParts);
if(!class_exists(sprintf('%s\\%s', CLASS_NAMESPACE, $className))) {
$classBody = sprintf("namespace %s;\n\n", CLASS_NAMESPACE)
. $behaviorUse
. "\nclass " . $className . " extends ComponentAbstract\n{"
. $behaviorCode
. "\n public function __construct(\$nameOrSpec=\"Generic " . implode(" ", $classNameParts) . " Component\", string \$version=null)\n"
. " {\n"
. " parent::__construct(\$nameOrSpec, \$version);\n\n"
. $behaviorInit
. " }\n}\n";
//echo $classBody;
echo "creating class " . $className . PHP_EOL;
eval($classBody);
}
} else {
throw new RuntimeException(sprintf("%s - no valid behaviors specified!", __METHOD__));
}
}
}
// test all variants of generating classes on the fly
polymorphic_class(['Price']);
polymorphic_class(['Size']);
polymorphic_class(['Size']); // make sure repetition skips trying to redefine
polymorphic_class(['Weight']);
polymorphic_class(['Price', 'Size']);
polymorphic_class(['Price', 'Weight']);
polymorphic_class(['Size', 'Weight']);
polymorphic_class(['Price', 'Size', 'Weight']);
// let's see if they exist now
foreach(['Price', 'Size', 'Weight', 'PriceSize', 'PriceWeight', 'SizeWeight', 'PriceSizeWeight'] as $suff) {
if(class_exists(CLASS_NAMESPACE.'\\Generic'.$suff))
echo "Generic".$suff." Exists\n";
}
输出:
creating class GenericPrice
creating class GenericSize
creating class GenericWeight
creating class GenericPriceSize
creating class GenericPriceWeight
creating class GenericSizeWeight
creating class GenericPriceSizeWeight
GenericPrice Exists
GenericSize Exists
GenericWeight Exists
GenericPriceSize Exists
GenericPriceWeight Exists
GenericSizeWeight Exists
GenericPriceSizeWeight Exists
Process finished with exit code 0