vendor/mondator/src/Mandango/Mondator/ClassExtension.php
changeset 18 c85b9d1ddf19
equal deleted inserted replaced
15:99ad73ef7385 18:c85b9d1ddf19
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of Mandango.
       
     5  *
       
     6  * (c) Pablo Díez <pablodip@gmail.com>
       
     7  *
       
     8  * This source file is subject to the MIT license that is bundled
       
     9  * with this source code in the file LICENSE.
       
    10  */
       
    11 
       
    12 namespace Mandango\Mondator;
       
    13 
       
    14 use Mandango\Mondator\Definition\Method;
       
    15 use Mandango\Mondator\Definition\Property;
       
    16 
       
    17 /**
       
    18  * ClassExtension is the base class for class extensions.
       
    19  *
       
    20  * @author Pablo Díez <pablodip@gmail.com>
       
    21  *
       
    22  * @api
       
    23  */
       
    24 abstract class ClassExtension
       
    25 {
       
    26     private $options;
       
    27     private $requiredOptions;
       
    28 
       
    29     protected $definitions;
       
    30 
       
    31     protected $class;
       
    32     protected $configClasses;
       
    33     protected $configClass;
       
    34 
       
    35     protected $newClassExtensions;
       
    36     protected $newConfigClasses;
       
    37 
       
    38     protected $twig;
       
    39     protected $twigTempDir;
       
    40 
       
    41     /**
       
    42      * Constructor.
       
    43      *
       
    44      * @param array $options An array of options.
       
    45      *
       
    46      * @api
       
    47      */
       
    48     public function __construct(array $options = array())
       
    49     {
       
    50         $this->options = array();
       
    51         $this->requiredOptions = array();
       
    52 
       
    53         $this->setUp();
       
    54 
       
    55         foreach ($options as $name => $value) {
       
    56             $this->setOption($name, $value);
       
    57         }
       
    58 
       
    59         // required options
       
    60         if ($diff = array_diff($this->requiredOptions, array_keys($options))) {
       
    61             throw new \RuntimeException(sprintf('%s requires the options: "%s".', get_class($this), implode(', ', $diff)));
       
    62         }
       
    63     }
       
    64 
       
    65     /**
       
    66      * Set up the extension.
       
    67      *
       
    68      * @api
       
    69      */
       
    70     protected function setUp()
       
    71     {
       
    72     }
       
    73 
       
    74     /**
       
    75      * Add an option.
       
    76      *
       
    77      * @param string $name         The option name.
       
    78      * @param mixed  $defaultValue The default value (optional, null by default).
       
    79      *
       
    80      * @api
       
    81      */
       
    82     protected function addOption($name, $defaultValue = null)
       
    83     {
       
    84         $this->options[$name] = $defaultValue;
       
    85     }
       
    86 
       
    87     /**
       
    88      * Add options.
       
    89      *
       
    90      * @param array $options An array with options (name as key and default value as value).
       
    91      *
       
    92      * @api
       
    93      */
       
    94     protected function addOptions(array $options)
       
    95     {
       
    96         foreach ($options as $name => $defaultValue) {
       
    97             $this->addOption($name, $defaultValue);
       
    98         }
       
    99     }
       
   100 
       
   101     /**
       
   102      * Add a required option.
       
   103      *
       
   104      * @param string $name The option name.
       
   105      *
       
   106      * @api
       
   107      */
       
   108     protected function addRequiredOption($name)
       
   109     {
       
   110         $this->addOption($name);
       
   111 
       
   112         $this->requiredOptions[] = $name;
       
   113     }
       
   114 
       
   115     /**
       
   116      * Add required options.
       
   117      *
       
   118      * @param array $options An array with the name of the required option as value.
       
   119      *
       
   120      * @api
       
   121      */
       
   122     protected function addRequiredOptions(array $options)
       
   123     {
       
   124         foreach ($options as $name) {
       
   125             $this->addRequiredOption($name);
       
   126         }
       
   127     }
       
   128 
       
   129     /**
       
   130      * Returns if exists an option.
       
   131      *
       
   132      * @param string $name The name.
       
   133      *
       
   134      * @return bool Returns true if the option exists, false otherwise.
       
   135      *
       
   136      * @api
       
   137      */
       
   138     public function hasOption($name)
       
   139     {
       
   140         return array_key_exists($name, $this->options);
       
   141     }
       
   142 
       
   143     /**
       
   144      * Set an option.
       
   145      *
       
   146      * @param string $name  The name.
       
   147      * @param mixed  $value The value.
       
   148      *
       
   149      * @throws \InvalidArgumentException If the option does not exists.
       
   150      *
       
   151      * @api
       
   152      */
       
   153     public function setOption($name, $value)
       
   154     {
       
   155         if (!$this->hasOption($name)) {
       
   156             throw new \InvalidArgumentException(sprintf('The option "%s" does not exists.', $name));
       
   157         }
       
   158 
       
   159         $this->options[$name] = $value;
       
   160     }
       
   161 
       
   162     /**
       
   163      * Returns the options.
       
   164      *
       
   165      * @return array The options.
       
   166      *
       
   167      * @api
       
   168      */
       
   169     public function getOptions()
       
   170     {
       
   171         return $this->options;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Return an option.
       
   176      *
       
   177      * @param string $name The name.
       
   178      *
       
   179      * @return mixed The value of the option.
       
   180      *
       
   181      * @throws \InvalidArgumentException If the options does not exists.
       
   182      *
       
   183      * @api
       
   184      */
       
   185     public function getOption($name)
       
   186     {
       
   187         if (!$this->hasOption($name)) {
       
   188             throw new \InvalidArgumentException(sprintf('The option "%s" does not exists.', $name));
       
   189         }
       
   190 
       
   191         return $this->options[$name];
       
   192     }
       
   193 
       
   194     /**
       
   195      * New class extensions process.
       
   196      *
       
   197      * @param string       $class              The class.
       
   198      * @param \ArrayObject $configClasses      The config classes.
       
   199      * @param \ArrayObject $newClassExtensions The new class extensions.
       
   200      *
       
   201      * @api
       
   202      */
       
   203     public function newClassExtensionsProcess($class, \ArrayObject $configClasses, \ArrayObject $newClassExtensions)
       
   204     {
       
   205         $this->class = $class;
       
   206         $this->configClasses = $configClasses;
       
   207         $this->configClass = $configClasses[$class];
       
   208         $this->newClassExtensions = $newClassExtensions;
       
   209 
       
   210         $this->doNewClassExtensionsProcess();
       
   211 
       
   212         $this->class = null;
       
   213         $this->configClasses = null;
       
   214         $this->configClass = null;
       
   215         $this->newClassExtensions = null;
       
   216     }
       
   217 
       
   218     /**
       
   219      * Do the new class extensions process.
       
   220      *
       
   221      * Here you can add new class extensions.
       
   222      *
       
   223      * @api
       
   224      */
       
   225     protected function doNewClassExtensionsProcess()
       
   226     {
       
   227     }
       
   228 
       
   229     /**
       
   230      * New config classes process.
       
   231      *
       
   232      * @param string       $class            The class.
       
   233      * @param \ArrayObject $configClasses    The config classes.
       
   234      * @param \ArrayObject $newConfigClasses The new config classes.
       
   235      *
       
   236      * @api
       
   237      */
       
   238     public function newConfigClassesProcess($class, \ArrayObject $configClasses, \ArrayObject $newConfigClasses)
       
   239     {
       
   240         $this->class = $class;
       
   241         $this->configClasses = $configClasses;
       
   242         $this->configClass = $configClasses[$class];
       
   243         $this->newConfigClasses = $newConfigClasses;
       
   244 
       
   245         $this->doNewConfigClassesProcess();
       
   246 
       
   247         $this->class = null;
       
   248         $this->configClasses = null;
       
   249         $this->configClass = null;
       
   250         $this->newConfigClasses = null;
       
   251     }
       
   252 
       
   253     /**
       
   254      * Do the new config classes process.
       
   255      *
       
   256      * Here you can add new config classes, and change the config classes
       
   257      * if it is necessary to build the new config classes.
       
   258      *
       
   259      * @api
       
   260      */
       
   261     protected function doNewConfigClassesProcess()
       
   262     {
       
   263     }
       
   264 
       
   265     /**
       
   266      * Process the config class.
       
   267      *
       
   268      * @param string       $class         The class.
       
   269      * @param \ArrayObject $configClasses The config classes.
       
   270      *
       
   271      * @api
       
   272      */
       
   273     public function configClassProcess($class, \ArrayObject $configClasses)
       
   274     {
       
   275         $this->class = $class;
       
   276         $this->configClasses = $configClasses;
       
   277         $this->configClass = $configClasses[$class];
       
   278 
       
   279         $this->doConfigClassProcess();
       
   280 
       
   281         $this->class = null;
       
   282         $this->configClasses = null;
       
   283         $this->configClass = null;
       
   284     }
       
   285 
       
   286     /**
       
   287      * Do the config class process.
       
   288      *
       
   289      * Here you can modify the config class.
       
   290      *
       
   291      * @api
       
   292      */
       
   293     protected function doConfigClassProcess()
       
   294     {
       
   295     }
       
   296 
       
   297 
       
   298     /**
       
   299      * Process the class.
       
   300      *
       
   301      * @param string                      $class         The class.
       
   302      * @param \ArrayObject                $configClasses The config classes.
       
   303      * @param Mandango\Mondator\Container $container     The container.
       
   304      *
       
   305      * @api
       
   306      */
       
   307     public function classProcess($class, \ArrayObject $configClasses, Container $container)
       
   308     {
       
   309         $this->class = $class;
       
   310         $this->configClasses = $configClasses;
       
   311         $this->configClass = $configClasses[$class];
       
   312         $this->definitions = $container;
       
   313 
       
   314         $this->doClassProcess();
       
   315 
       
   316         $this->class = null;
       
   317         $this->configClasses = null;
       
   318         $this->configClass = null;
       
   319         $this->definitions = null;
       
   320     }
       
   321 
       
   322     /**
       
   323      * Do the class process.
       
   324      *
       
   325      * @api
       
   326      */
       
   327     protected function doClassProcess()
       
   328     {
       
   329     }
       
   330 
       
   331     /**
       
   332      * Twig.
       
   333      */
       
   334     protected function processTemplate(Definition $definition, $name, array $variables = array())
       
   335     {
       
   336         $twig = $this->getTwig();
       
   337 
       
   338         $variables['options'] = $this->options;
       
   339         $variables['class'] = $this->class;
       
   340         $variables['config_class'] = $this->configClass;
       
   341         $variables['config_classes'] = $this->configClasses;
       
   342 
       
   343         $result = $twig->loadTemplate($name)->render($variables);
       
   344 
       
   345         // properties
       
   346         $expression = '/
       
   347             (?P<docComment>\ \ \ \ \/\*\*\n[\s\S]*\ \ \ \ \ \*\/)?\n?
       
   348              \ \ \ \ (?P<static>static\ )?
       
   349             (?P<visibility>public|protected|private)
       
   350             \s
       
   351             \$
       
   352             (?P<name>[a-zA-Z0-9_]+)
       
   353             ;
       
   354         /xU';
       
   355         preg_match_all($expression, $result, $matches);
       
   356 
       
   357         for ($i = 0; $i <= count($matches[0]) - 1; $i++) {
       
   358             $property = new Property($matches['visibility'][$i], $matches['name'][$i], null);
       
   359             if ($matches['static'][$i]) {
       
   360                 $property->setStatic(true);
       
   361             }
       
   362             if ($matches['docComment'][$i]) {
       
   363                 $property->setDocComment($matches['docComment'][$i]);
       
   364             }
       
   365             $definition->addProperty($property);
       
   366         }
       
   367 
       
   368         // methods
       
   369         $expression = '/
       
   370             (?P<docComment>\ \ \ \ \/\*\*\n[\s\S]*\ \ \ \ \ \*\/)?\n
       
   371             \ \ \ \ (?P<static>static\ )?
       
   372             (?P<visibility>public|protected|private)
       
   373             \s
       
   374             function
       
   375             \s
       
   376             (?P<name>[a-zA-Z0-9_]+)
       
   377             \((?P<arguments>[$a-zA-Z0-9_\\\=\(\), ]*)\)
       
   378             \n
       
   379             \ \ \ \ \{
       
   380                 (?P<code>[\s\S]*)
       
   381             \n\ \ \ \ \}
       
   382         /xU';
       
   383         preg_match_all($expression, $result, $matches);
       
   384 
       
   385         for ($i = 0; $i <= count($matches[0]) - 1; $i++) {
       
   386             $code = trim($matches['code'][$i], "\n");
       
   387             $method = new Method($matches['visibility'][$i], $matches['name'][$i], $matches['arguments'][$i], $code);
       
   388             if ($matches['static'][$i]) {
       
   389                 $method->setStatic(true);
       
   390             }
       
   391             if ($matches['docComment'][$i]) {
       
   392                 $method->setDocComment($matches['docComment'][$i]);
       
   393             }
       
   394             $definition->addMethod($method);
       
   395         }
       
   396     }
       
   397 
       
   398     public function getTwig()
       
   399     {
       
   400         if (null === $this->twig) {
       
   401             if (!class_exists('Twig_Environment')) {
       
   402                 throw new \RuntimeException('Twig is required to use templates.');
       
   403             }
       
   404 
       
   405             $loader = new \Twig_Loader_String();
       
   406             $twig = new \Twig_Environment($loader, array(
       
   407                 'autoescape'       => false,
       
   408                 'strict_variables' => true,
       
   409                 'debug'            => true,
       
   410                 'cache'            => $this->twigTempDir = sys_get_temp_dir().'Mondator/'.mt_rand(111111, 999999),
       
   411             ));
       
   412 
       
   413             $this->configureTwig($twig);
       
   414 
       
   415             $this->twig = $twig;
       
   416         }
       
   417 
       
   418         return $this->twig;
       
   419     }
       
   420 
       
   421     protected function configureTwig(\Twig_Environment $twig)
       
   422     {
       
   423     }
       
   424 
       
   425     /*
       
   426      * Tools.
       
   427      */
       
   428     protected function createClassExtensionFromArray(array $data)
       
   429     {
       
   430         if (!isset($data['class'])) {
       
   431             throw new \InvalidArgumentException(sprintf('The extension does not have class.'));
       
   432         }
       
   433 
       
   434         return new $data['class'](isset($data['options']) ? $data['options'] : array());
       
   435     }
       
   436 
       
   437     private function removeDir($target)
       
   438     {
       
   439         $fp = opendir($target);
       
   440         while (false !== $file = readdir($fp)) {
       
   441             if (in_array($file, array('.', '..'))) {
       
   442                 continue;
       
   443             }
       
   444 
       
   445             if (is_dir($target.'/'.$file)) {
       
   446                 self::removeDir($target.'/'.$file);
       
   447             } else {
       
   448                 unlink($target.'/'.$file);
       
   449             }
       
   450         }
       
   451         closedir($fp);
       
   452         rmdir($target);
       
   453     }
       
   454 
       
   455     public function __destruct()
       
   456     {
       
   457         if ($this->twigTempDir && is_dir($this->twigTempDir)) {
       
   458             $this->removeDir($this->twigTempDir);
       
   459         }
       
   460     }
       
   461 }