digipolisgent/robo-digipolis-general

View on GitHub
src/DetermineProjectRoot.php

Summary

Maintainability
A
25 mins
Test Coverage
A
100%
<?php

namespace DigipolisGent\Robo\Task\General;

use Robo\Result;
use Robo\Task\BaseTask;
use Symfony\Component\Finder\Finder;

/**
 * Determines the root of a web project and adds it to the config.
 * Config is available for classes implementing
 * \Robo\Contract\ConfigAwareInterface and using \Robo\Common\ConfigAwareTrait.
 *
 * ``` php
 * $this->taskDetermineProjectRoot()
 *     ->dir(getcwd())
 *     ->run();
 * $this->getConfig()->get('digipolis.root.project');
 * ```
 *
 */
class DetermineProjectRoot extends BaseTask
{
    /**
     * The directory in which to search for the project root.
     *
     * @var string
     */
    protected $dir;

    /**
     * Directories or files to exclude.
     *
     * @var string
     */
    protected $exclude = [];

    /**
     * The maximum depth to traverse directories.
     *
     * @var int
     */
    protected $depth;

    /**
     * The Symfony finder to use to find the project root.
     *
     * @var \Symfony\Component\Finder\Finder
     */
    protected $finder;

    /**
     * The file names that determine the root dir.
     *
     * @var array
     */
    protected $searchFiles = ['properties.yml', 'composer.json'];

    /**
     * The config key where the root should be saved.
     *
     * @var string
     */
    protected $configKey = 'digipolis.root.project';

    /**
     * Creates a DetermineProjectRoot object.
     *
     * @param string $dir
     *   The directory in which to search for the project root.
     */
    public function __construct($dir = null, $depth = 2)
    {
        $this->dir = is_null($dir) ? getcwd() : realpath($dir);
        $this->depth = $depth;
        $this->finder = new Finder();
    }

    /**
     * Sets the directory in which to search for the project root.
     *
     * @param string $dir
     *   The directory to set.
     *
     * @return $this
     *
     * @codeCoverageIgnore
     */
    public function dir($dir)
    {
        $this->dir = realpath($dir);

        return $this;
    }

    /**
     * Exclude certain directories or files.
     *
     * @param string|array $dirs
     *   A directory path or an array of directories
     *
     * @return $this
     *
     * @codeCoverageIgnore
     */
    public function exclude($dirs)
    {
        $this->exclude = array_merge($this->exclude, (array) $dirs);

        return $this;
    }

    /**
     * Sets the finder.
     *
     * @param \Symfony\Component\Finder\Finder $finder
     *
     * @return $this
     *
     * @codeCoverageIgnore
     */
    public function finder(Finder $finder)
    {
        $this->finder = $finder;

        return $this;
    }

    /**
     * Sets the search files.
     *
     * @param array $searchFiles
     *   The files to search for when searching for the project root.
     *
     * @return $this
     *
     * @codeCoverageIgnore
     */
    public function searchFiles(array $searchFiles)
    {
        $this->searchFiles = $searchFiles;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function run()
    {
        $finder = clone $this->finder;
        $finder->in([$this->dir])->exclude($this->exclude)->depth('<=' . $this->depth)->files();
        $rootCandidates = [];
        foreach ($this->searchFiles as $searchFile) {
            $fileFinder = clone $finder;
            $fileFinder->name($searchFile);
            foreach ($fileFinder->getIterator() as $file) {
                $rootCandidates[] = dirname($file->getRealPath());
            }
            if ($rootCandidates) {
                break;
            }
        }
        usort(
            $rootCandidates,
            function ($a, $b) {
                return count(explode(DIRECTORY_SEPARATOR, $a)) - count(explode(DIRECTORY_SEPARATOR, $b));
            }
        );
        $root =  $rootCandidates ? reset($rootCandidates) : getcwd();
        $this->printTaskInfo(sprintf('Setting %s to %s.', $this->configKey, $root));
        $this->getConfig()->set($this->configKey, $root);

        return Result::success($this, 'Found root at ' . $root . '.');
    }
}