Editors Note, February 14th 2022: This project is now ABANDONED and no longer supported or updated.

asherwunk/phabstractic implements an Abstract Factory pattern. To quote Wikipedia:

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.[1] In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part of the theme. The client doesn’t know (or care) which concrete objects it gets from each of these internal factories, since it uses only the generic interfaces of their products.[1] This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface.[2]

Abstract Factories

The abstract factory class in the link creates other abstract factories. This is much like the Enumeration data type which creates constants/enumerators with specific arguments. Each Abstract Factory is a throw-away object that you can use to create code dynamically for a given factory. The idea is that you edit or work with the object, setting methods, setting constants, etc., and then ‘bake’ the object so that a new AbstractFactory is defined.

Every Abstract Factory has a name, namespace, constants, and methods. The factory name is a string and will be prefixed with “Abstract” in the class name. The namespace is simply where we can find this. Constants are stored in an array (so they can have a key) and methods are stored in a unique set:

namespace Phabstractic\Patterns\Resource
{
    class AbstractFactory implements FeaturesResource\ConfigurationInterface {
        use Features\ConfigurationTrait;
        
        /**
         * Keeps track of already defined AbstractFactory classes internally
         * 
         * @var Phabstractic\Data\Types\Set
         */
        private static $factories = null;
        
        /**
         * The name of the factory to be generated
         * 
         * @var string
         */
        private $factoryName = '';
        
        /**
         * Whether the class has defined itself
         * 
         * @var bool
         */
        private $baked = false;
        
        /**
         * The constants, name => value, of the enumeration
         * 
         * @var array
         */
        private $constants = null;
        
        /**
         * The abstract methods (turned into-> makeMethodName)
         * 
         * @var Phabstractic\Data\Types\Set
         */
        private $methods = null;
        
        /**
         * The namespace under which the abstract factory class is to be defined
         * 
         * @var string
         */
        private $namespace = '';
        
        public function __construct(
            $factoryName,
            array $methods = array(),
            array $constants = array(),
            array $options = array()
        ) {
            $this->configure($options);
            
            $this->setUpFactories();
            
            // get FQN before to check against self::factories
            if (isset($this->conf->namespace) && $this->conf->namespace) {
                $this->setNamespace($this->conf->namespace);
            }
            
            $this->setFactoryName($factoryName);
            
            // method names must be unique
            $this->methods = new Types\Set($methods,
                                           array('strict'=>true,'unique'=>true));
            
            // constants must be unique
            $this->constants = $constants;
            
            if (isset($this->conf->bake) && $this->conf->bake) {
                $this->bake();
            }
        }
    }
}

Code generation has changed since version 2.  In version 2 the code generating function had its own scoped options to contend with.  The object’s main options were re-mapped onto the functions options.  The function in version 3 now uses the object’s options instead of its own.  The code for the code generation lays itself out as such:

private function createAbstractFactory() {
    // Start with blank code
    $classCode = '';
    
    /* Place namespace identifier at top of 'code', as eval statements
       operate outside of the namespace context of the code executing
       the eval statement */
    if (isset($this->conf->namespace) && $this->conf->namespace) {
        $classCode .= 'namespace ' . $this->conf->namespace . "{\n\n";
    }
    
    /* turns factoryName into Abstract{FactoryName}Factory so,
       Book becomes AbstractBookFactory */
    $classCode .= 'abstract class Abstract' . ucfirst($this->factoryName) .
                  'Factory {' . "\n";
    
    foreach ($this->constants as $identifier => $val) {
        // Any class constants (transformed into uppercase)
        $classCode .= 'const ' . strtoupper($identifier) . " = $val;\n\t";
    }
    
    foreach ($this->methods->iterate() as $method) {
        // make the abstract methods, method is turned into makeMethod
        $classCode .= 'abstract public function make' .
                      ucfirst($method)  . "();\n\t";
    }
    
    if (isset($this->conf->namespace) && $this->conf->namespace) {
        $classCode .= '}';
    }
    
    $classCode .= '}';
    
    try {
        eval($classCode);
    } catch (\Exception $e) {
        throw new PatternsException\CodeGenerationException(
            'Phabstractic\\Patterns\\Resource\\AbstractFactory->createAbstractFactory: ' .
            'Unable to generate ' . $this->factoryName .
            ' due to an internal error.');
    }
    
    return true;  // The abstract factory was generated
}

As you can see all the constants are pushed to uppercase. Each method is prefixed with ‘make’. The thing to note is that this creates an abstract class. You inherit this class and fill in the details for each method. This just lays out the skeleton for you to build off of. You can also use a static method to skip the throw-away object entirely:

public static function buildAbstractFactory(
    $factoryName,
    array $methods,
    array $constants = array(),
    array $options = array()
) {
    // preserve other options
    $options['bake'] = true;
    
    $factory = new AbstractFactory(
        $factoryName,
        $methods,
        $constants,
        $options
    );
    
    return $factory;
}

This calls the constructor and fills the $factory variable with the throw-away object, which is then, well, thrown away.

(View on github)

This is part of the Phabstractic Library.

photo credit: CTRL via photopin (license)

Asher Wolfstein

Metaverse Resident

About the Author

A metaverse resident, you can find me on Second Life (kadar.talbot) and other online platforms. I write about my digital life, my musings, and my projects as a programmer, webmaster, artist, and game designer. (exist (be wunk) (use rational imagination) (import artist coder maker furry) (conditional (if (eq you asshole) (me (block you))))

View Articles