Overview

Namespaces

  • GAubry
    • ErrorHandler
    • Helpers
    • Logger
    • Shell
  • Himedia
    • Padocc
      • DB
      • Minifier
      • Numbering
      • Properties
      • Task
        • Base
        • Extended
  • None
  • Psr
    • Log

Classes

  • AttributeProperties
  • Deployment
  • DeploymentStatus
  • DIContainer
  • Padocc
  • Task

Interfaces

  • DIContainerInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: 
  3: namespace Himedia\Padocc;
  4: 
  5: use GAubry\Shell\ShellAdapter;
  6: use Himedia\Padocc\Numbering\NumberingInterface;
  7: use Himedia\Padocc\Properties\PropertiesInterface;
  8: use Himedia\Padocc\Task\Base\Project;
  9: use Psr\Log\LoggerInterface;
 10: use Psr\Log\NullLogger;
 11: 
 12: /**
 13:  * @author Geoffroy AUBRY <gaubry@hi-media.com>
 14:  */
 15: abstract class Task
 16: {
 17: 
 18:     /**
 19:      * Compteur d'instances pour mieux s'y retrouver dans les logs des tâches.
 20:      * @var NumberingInterface
 21:      * @see $sName
 22:      */
 23:     protected $oNumbering;
 24: 
 25:     /**
 26:      * Collection de services.
 27:      *
 28:      * @var DIContainerInterface
 29:      */
 30:     protected $oDIContainer;
 31: 
 32:     /**
 33:      * Instance de AttributeProperties.
 34:      * @var AttributeProperties
 35:      * @see check()
 36:      */
 37:     protected $oAttrProperties;
 38: 
 39:     /**
 40:      * @var ShellAdapter Shell adapter
 41:      */
 42:     protected $oShell;
 43: 
 44:     /**
 45:      * @var LoggerInterface Logger adapter
 46:      */
 47:     private $oLogger;
 48: 
 49:     /**
 50:      * @var array
 51:      */
 52:     protected $aConfig;
 53: 
 54:     /**
 55:      * @var PropertiesInterface Properties adapter
 56:      */
 57:     protected $oProperties;
 58: 
 59:     /**
 60:      * Contenu XML de la tâche.
 61:      * @var \SimpleXMLElement
 62:      */
 63:     protected $oXMLTask;
 64: 
 65:     /**
 66:      * @var Project
 67:      */
 68:     protected $oProject;
 69: 
 70:     /**
 71:      * Chaîne numérotant la tâche.
 72:      * @var string
 73:      * @see NumberingInterface::getNextCounterValue()
 74:      */
 75:     protected $sCounter;
 76: 
 77:     /**
 78:      * Nom complet de la tâche, utilisé notamment dans le suivi d'exécution.
 79:      * @var string
 80:      */
 81:     protected $sName;
 82: 
 83:     /**
 84:      * Attributs XML de la tâche.
 85:      * Tableau ((string) clé, (string) valeur).
 86:      * @var array
 87:      */
 88:     protected $aAttValues;
 89: 
 90:     /**
 91:      * Liste des propriétés des attributs déclarés de la tâche.
 92:      *
 93:      * Structure : array('attribute' => iValue, ...)
 94:      * Où iValue vaut 0 ou une combinaison de bits au sens |,
 95:      * à partir des constantes de la classe AttributeProperties.
 96:      *
 97:      * @var array
 98:      * @see check()
 99:      * @see AttributeProperties
100:      */
101:     protected $aAttrProperties;
102: 
103:     /**
104:      * Retourne le nom du tag XML correspondant à cette tâche dans les config projet.
105:      *
106:      * @return string nom du tag XML correspondant à cette tâche dans les config projet.
107:      * @throws \RuntimeException si appelée directement sur Task.
108:      */
109:     public static function getTagName ()
110:     {
111:         throw new \RuntimeException('Not implemented at this level!');
112:     }
113: 
114:     /**
115:      * Surcharge du constructeur, dont le premier paramètre est passé d'une instance de \SimpleXMLElement à
116:      * un tableau associatif attribut => valeur.
117:      * Ne peut être utilisé pour créer une instance de Project.
118:      *
119:      * @param array $aAttributes Tableau associatif listant des attributs et leur valeur.
120:      * @param Project $oProject Super tâche projet.
121:      * @param DIContainerInterface $oDIContainer Register de services prédéfinis (ShellInterface, ...).
122:      * @return Task
123:      * @throws \RuntimeException si appelée directement sur Task.
124:      */
125:     public static function getNewInstance (array $aAttributes, Project $oProject, DIContainerInterface $oDIContainer)
126:     {
127:         $sAttributes = '';
128:         foreach ($aAttributes as $sName => $sValue) {
129:             $sAttributes .= ' ' . $sName . '="' . $sValue . '"';
130:         }
131:         $sXML = '<' . static::getTagName() . $sAttributes . ' />';
132: 
133:         $oXML = new \SimpleXMLElement($sXML);
134:         return new static($oXML, $oProject, $oDIContainer);
135:     }
136: 
137:     /**
138:      * Constructeur.
139:      * @param \SimpleXMLElement $oXMLTask Contenu XML de la tâche.
140:      * @param Project $oProject Super tâche projet.
141:      * @param DIContainerInterface $oDIContainer Register de services prédéfinis (ShellInterface, ...).
142:      */
143:     public function __construct (\SimpleXMLElement $oXMLTask, Project $oProject, DIContainerInterface $oDIContainer)
144:     {
145:         $this->oXMLTask = $oXMLTask;
146:         $this->oProject = $oProject;
147: 
148:         $this->oDIContainer = $oDIContainer;
149:         $this->aConfig = $oDIContainer->getConfig();
150: 
151:         $this->setShellAdapter($oDIContainer->getShellAdapter());
152:         $this->setNumberingAdapter($oDIContainer->getNumberingAdapter());
153:         $this->setPropertiesAdapter($oDIContainer->getPropertiesAdapter());
154: 
155:         if ($logger = $oDIContainer->getLogger()) {
156:             $this->setLogger($logger);
157:         }
158: 
159:         // TODO à injecter :
160:         $this->setAttributePropertiesAdapter(new AttributeProperties($this->oShell));
161: 
162:         $sCounter = $this->oNumbering->getNextCounterValue();
163:         $this->sCounter = $sCounter;
164:         $this->sName = (strlen($this->sCounter) === 0 ? '' : $this->sCounter . '_') . get_class($this);
165: 
166:         $this->aAttrProperties = array();
167:         $this->fetchAttributes();
168: 
169:         $this->init();
170:     }
171: 
172:     /**
173:      * Initializes task.
174:      *
175:      * @return void
176:      */
177:     protected function init()
178:     {
179:     }
180: 
181:     /**
182:      * Gets the logger.
183:      *
184:      * @return LoggerInterface
185:      */
186:     public function getLogger()
187:     {
188:         if ($this->oLogger === null) {
189:             $this->oLogger = new NullLogger();
190:         }
191: 
192:         return $this->oLogger;
193:     }
194: 
195:     /**
196:      * Sets a logger.
197:      *
198:      * @param LoggerInterface $logger
199:      */
200:     public function setLogger(LoggerInterface $logger)
201:     {
202:         $this->oLogger = $logger;
203:     }
204: 
205:     /**
206:      * Sets the Shell adapter.
207:      *
208:      * @param ShellAdapter $adapter
209:      */
210:     public function setShellAdapter(ShellAdapter $adapter)
211:     {
212:         $this->oShell = $adapter;
213:     }
214: 
215:     /**
216:      * Sets the numbering adapter.
217:      *
218:      * @param NumberingInterface $adapter
219:      */
220:     public function setNumberingAdapter(NumberingInterface $adapter)
221:     {
222:         $this->oNumbering = $adapter;
223:     }
224: 
225:     /**
226:      * Sets the properties adapter.
227:      *
228:      * @param PropertiesInterface $adapter
229:      */
230:     public function setPropertiesAdapter(PropertiesInterface $adapter)
231:     {
232:         $this->oProperties = $adapter;
233:     }
234: 
235:     /**
236:      * Sets the attribute properties adapter.
237:      *
238:      * @param AttributeProperties $adapter
239:      */
240:     public function setAttributePropertiesAdapter(AttributeProperties $adapter)
241:     {
242:         $this->oAttrProperties = $adapter;
243:     }
244: 
245:     /**
246:      * Récupère les attributs XML du nœud $this->oXMLTask et les enregistre dans $this->aAttValues.
247:      */
248:     protected function fetchAttributes ()
249:     {
250:         $this->aAttValues = array();
251:         foreach ($this->oXMLTask->attributes() as $key => $val) {
252:             $this->aAttValues[$key] = (string)$val;
253:         }
254:     }
255: 
256:     /**
257:      * Appels combinés à expandPath() puis reroutePaths()
258:      *
259:      * @param string $sPath chemin pouvant contenir des paramètres
260:      * @return array liste de tous les chemins générés en remplaçant les paramètres par leurs valeurs
261:      * et en reroutant ceux tombant dans 'basedir'.
262:      * @see expandPath()
263:      * @see reroutePaths()
264:      */
265:     protected function processPath ($sPath)
266:     {
267:         $aExpandedPaths = $this->expandPath($sPath);
268:         $aReroutedPaths = $this->reroutePaths($aExpandedPaths);
269:         return $aReroutedPaths;
270:     }
271: 
272:     /**
273:      * Appel à processPath(), puis retourne le premier chemin récupéré
274:      * en s'assurant qu'il n'y en a pas d'autres.
275:      *
276:      * @param string $sPath chemin pouvant contenir des paramètres
277:      * @return string l'unique chemin généré en remplaçant les paramètres par leurs valeurs
278:      * et en reroutant le chemin s'il tombe dans 'basedir'.
279:      * @throws \RuntimeException si plus d'un chemin a été généré
280:      * @see processPath()
281:      */
282:     protected function processSimplePath ($sPath)
283:     {
284:         $aProcessedPaths = $this->processPath($sPath);
285:         if (count($aProcessedPaths) !== 1) {
286:             $sMsg = "String '$sPath' should return a single path after process: " . print_r($aProcessedPaths, true);
287:             throw new \RuntimeException($sMsg);
288:         }
289:         return reset($aProcessedPaths);
290:     }
291: 
292:     /**
293:      * Retourne la liste de tous les chemins générés en remplaçant les paramètres
294:      * du chemin spécifié par leurs valeurs.
295:      *
296:      * @param string $sPath chemin pouvant contenir des paramètres
297:      * @return array liste de tous les chemins générés en remplaçant les paramètres par leurs valeurs,
298:      */
299:     protected function expandPath ($sPath)
300:     {
301:         if (preg_match_all('/\$\{([^}]+)\}/', $sPath, $aMatches) > 0) {
302:             // On traite dans un premier temps un maximum de remplacements sans récursivité :
303:             $aPaths = array($sPath);
304:             foreach ($aMatches[1] as $property) {
305:                 $aToProcessPaths = $aPaths;
306:                 $aPaths = array();
307: 
308:                 $sRawValue = $this->oProperties->getProperty($property);
309:                 $values = explode(' ', $sRawValue);
310:                 foreach ($aToProcessPaths as $sPath) {
311:                     foreach ($values as $value) {
312:                         $aPaths[] = str_replace('${' . $property . '}', $value, $sPath);
313:                     }
314:                 }
315:             }
316: 
317:             // Perfectible mais suffisant, récursivité sur les propriétés de propriétés :
318:             $aRecursivePaths = $aPaths;
319:             $aPaths = array();
320:             foreach ($aRecursivePaths as $sPath) {
321:                 $aPaths = array_merge($aPaths, $this->expandPath($sPath));
322:             }
323:             $aPaths = array_values(array_unique($aPaths));
324:         } else {
325:             $aPaths = array($sPath);
326:         }
327: 
328:         // Set default remote user if not specified:
329:         foreach ($aPaths as $idx => $sPath) {
330:             if (preg_match('#^[^:@]+:(?!//).+$#i', $sPath) === 1) {
331:                 $aPaths[$idx] = $this->aConfig['default_remote_shell_user'] . '@' . $sPath;
332:             }
333:         }
334:         return $aPaths;
335:     }
336: 
337:     /**
338:      * Reroute de façon transparente tous les chemins système inclus ou égal à la valeur de la propriété 'basedir'
339:      * dans le répertoire de releases nommé de la valeur de 'basedir'
340:      * avec le suffixe $aConfig['symlink_releases_dir_suffix'].
341:      * Les autres chemins, ceux hors 'basedir', restent inchangés.
342:      *
343:      * @param array $aPaths liste de chemins sans paramètres (par exemple provenant de expandPath())
344:      * @return array liste de ces mêmes chemins en reroutant ceux tombant dans 'basedir'.
345:      */
346:     protected function reroutePaths (array $aPaths)
347:     {
348:         if ($this->oProperties->getProperty('with_symlinks') === 'true') {
349:             $sBaseSymLink = $this->oProperties->getProperty('basedir');
350:             $sReleaseSymLink = $sBaseSymLink . $this->aConfig['symlink_releases_dir_suffix'] . '/'
351:                              . $this->oProperties->getProperty('execution_id');
352:             for ($i=0, $iMax=count($aPaths); $i<$iMax; $i++) {
353:                 if (preg_match('#^(.*?:)' . preg_quote($sBaseSymLink, '#') . '\b#', $aPaths[$i], $aMatches) === 1) {
354:                     $sNewPath = str_replace(
355:                         $aMatches[1] . $sBaseSymLink,
356:                         $aMatches[1] . $sReleaseSymLink,
357:                         $aPaths[$i]
358:                     );
359:                     $aPaths[$i] = $sNewPath;
360:                 }
361:             }
362:         }
363:         return $aPaths;
364:     }
365: 
366:     /**
367:      * Centralisation de tous les chemins systèmes définis dans l'une ou l'autre des tâches.
368:      * Dédoublonnés et triés par ordre alphabétique.
369:      * Structure : array((string)path => true, ...)
370:      * @var array
371:      * @see registerPaths()
372:      */
373:     protected static $aRegisteredPaths = array();
374: 
375:     /**
376:      * Collecte les chemins système définis dans les attributs de la tâche,
377:      * et les centralise au niveau de la classe pour analyse ultérieure.
378:      */
379:     protected function registerPaths ()
380:     {
381:         foreach ($this->aAttrProperties as $sAttribute => $iProperties) {
382:             if ((($iProperties & AttributeProperties::DIR) > 0 || ($iProperties & AttributeProperties::FILE) > 0)
383:                 && isset($this->aAttValues[$sAttribute])
384:             ) {
385:                 self::$aRegisteredPaths[$this->aAttValues[$sAttribute]] = true;
386:             }
387:         }
388:         ksort(self::$aRegisteredPaths);
389:     }
390: 
391:     /**
392:      * Prépare la tâche avant exécution : vérifications basiques, analyse des serveurs concernés...
393:      */
394:     public function setUp ()
395:     {
396:         $this->check();
397:         $this->registerPaths();
398:     }
399: 
400:     /**
401:      * Vérifie au moyen de tests basiques que la tâche peut être exécutée.
402:      * Lance une exception si tel n'est pas le cas.
403:      *
404:      * Comme toute les tâches sont vérifiées avant que la première ne soit exécutée,
405:      * doit permettre de remonter au plus tôt tout dysfonctionnement.
406:      * Appelé avant la méthode execute().
407:      *
408:      * @throws \UnexpectedValueException en cas d'attribut ou fichier manquant
409:      * @throws \DomainException en cas d'attribut non permis
410:      * @see self::$aAttributeProperties
411:      */
412:     protected function check ()
413:     {
414:         $sMsg = "Check '" . $this->sName . "' task";
415:         if (! empty($this->aAttValues['name'])) {
416:             $sMsg .= ': \'' . $this->aAttValues['name'] . '\'';
417:         }
418:         $this->getLogger()->info($sMsg . '+++');
419:         $this->oAttrProperties->checkAttributes($this->aAttrProperties, $this->aAttValues);
420:         $this->getLogger()->info('---');
421:     }
422: 
423:     /**
424:      * Phase de pré-traitements de l'exécution de la tâche.
425:      * Elle devrait systématiquement commencer par "parent::preExecute();".
426:      * Appelé par execute().
427:      * @see execute()
428:      */
429:     protected function preExecute ()
430:     {
431:         $sMsg = "Execute '" . $this->sName . "' task";
432:         if (! empty($this->aAttValues['name'])) {
433:             $sMsg .= ': \'' . $this->aAttValues['name'] . '\'';
434:         }
435:         $this->getLogger()->info($sMsg);
436:     }
437: 
438:     /**
439:      * Phase de traitements centraux de l'exécution de la tâche.
440:      * Elle devrait systématiquement commencer par "parent::centralExecute();".
441:      * Appelé par execute().
442:      * @see execute()
443:      * @codeCoverageIgnore
444:      */
445:     protected function centralExecute ()
446:     {
447:     }
448: 
449:     /**
450:      * Phase de post-traitements de l'exécution de la tâche.
451:      * Elle devrait systématiquement finir par "parent::postExecute();".
452:      * Appelé par execute().
453:      * @see execute()
454:      * @codeCoverageIgnore
455:      */
456:     protected function postExecute ()
457:     {
458:     }
459: 
460:     /**
461:      * Exécute la tâche en trois phases : pré-traitements, traitements centraux et post-traitements.
462:      * Si l'on a la classe F fille de la tâche P, alors on peut s'attendre à :
463:      *      P::preExecute()
464:      *      F::preExecute()
465:      *      P::centralExecute()
466:      *      F::centralExecute()
467:      *      F::postExecute()
468:      *      P::postExecute()
469:      *
470:      * @see preExecute()
471:      * @see centralExecute()
472:      * @see postExecute()
473:      */
474:     public function execute ()
475:     {
476:         $this->preExecute();
477:         $this->centralExecute();
478:         $this->postExecute();
479:     }
480: }
481: 
Platform for Automatized Deployments with pOwerful Concise Configuration API documentation generated by ApiGen 2.8.0