digipolisgent/robo-digipolis-deploy

View on GitHub
src/PartialCleanDirs.php

Summary

Maintainability
A
1 hr
Test Coverage
F
59%
<?php

namespace DigipolisGent\Robo\Task\Deploy;

use Robo\Task\BaseTask;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;

class PartialCleanDirs extends BaseTask
{
    /**
     * Sort by name.
     *
     * @const PartialCleanDir::SORT_NAME Sort by name.
     */
    const SORT_NAME = 'name';

    /**
     * This is the time that the file was last accessed, read or written to.
     *
     * @const PartialCleanDir::SORT_ACCESS_TIME Sort by access time.
     */
    const SORT_ACCESS_TIME = 'access_time';

    /**
     * This is the last time the actual contents of the file were last modified.
     *
     * @const PartialCleanDir::SORT_ACCESS_TIME Sort by modified time.
     */
    const SORT_MODIFIED_TIME = 'modified_time';

    /**
     * This is the time that the inode information was last modified
     * (permissions, owner, group or other metadata).
     *
     * @const PartialCleanDir::SORT_CHANGED_TIME Sort by changed time.
     */
    const SORT_CHANGED_TIME = 'changed_time';

    /**
     * Sorts files and directories by type (directories before files),then by
     * name.
     *
     * @const PartialCleanDir::SORT_TYPE Sort by type.
     */
    const SORT_TYPE = 'type';

    /**
     * The directories to clean.
     *
     * @var string
     */
    protected $dirs = [];

    /**
     * The finder used to get the files from the source directories.
     *
     * @var \Symfony\Component\Finder\Finder
     */
    protected $finder;

    /**
     * Filesystem component.
     *
     * @var \Symfony\Component\Filesystem\Filesystem
     */
    protected $fs;

    /**
     * What the sort the items in the directories by.
     *
     * @var string|\Closure
     */
    protected $sort;

    /**
     * Creates a new PartialCleanDir task.
     *
     * @param array $dirs
     *   The directories to clean. Either an array of directories or an array
     *   keyed by directory with the number of items to keep within this
     *   directory as value.
     * @param null|\Symfony\Component\Finder\Finder $finder
     *   The finder used to get the files from the source folder.
     * @param null|\Symfony\Component\Filesystem\Filesystem $fs
     *   Filesystem component to manipulate files.
     */
    public function __construct(array $dirs, Finder $finder = null, Filesystem $fs = null)
    {
        $this->dirs($dirs);
        $this->finder = is_null($finder)
            ? (new Finder())->ignoreDotFiles(false)->ignoreVCS(false)
            : $finder;
        $this->fs = is_null($fs)
            ? new Filesystem()
            : $fs;
        $this->sort = static::SORT_NAME;
    }

    /**
     * Set the finder to use.
     *
     * @param Finder $finder
     *
     * @return $this
     */
    public function finder(Finder $finder)
    {
        $this->finder = $finder;

        return $this;
    }

    /**
     * Set the filesystem component to use.
     *
     * @param Filesystem $fs
     *
     * @return $this
     */
    public function fileSystem(Filesystem $fs)
    {
        $this->fs = $fs;

        return $this;
    }

    /**
     * Add directories to clean.
     *
     * @param array $dirs
     *   Either an array of directories or an array keyed by directory with the
     *   number of items to keep within this directory as value.
     *
     * @return $this
     */
    public function dirs(array $dirs)
    {
        foreach ($dirs as $k => $v) {
            if (is_numeric($v)) {
                $this->dir($k, $v);
                continue;
            }
            $this->dir($v);
        }

        return $this;
    }

    /**
     * Add a directory to clean.
     *
     * @param $dir
     *   The directory to clean.
     * @param $keep
     *   The number of items to keep in this directory. Defaults to 5.
     *
     * @return $this
     */
    public function dir($dir, $keep = 5)
    {
        $this->dirs[$dir] = $keep;

        return $this;
    }

    /**
     * What to sort the folder items by. The last x items (as set by the dir()
     * method) returned after the sort method will be kept. When using one of
     * the PartialCleanDir::SORT_* constants, items will be sorted ascending. To
     * overwrite this behavior you should provide your own sort function as the
     * $sort parameter.
     *
     * @param string|\Closure $sort
     *   One of the PartialCleanDir::SORT_* constants or an anonymous function. The
     *   anonymous function receives two \SplFileInfo instances to compare.
     *
     * @return $this
     */
    public function sortBy($sort = self::SORT_NAME)
    {
        $this->sort = $sort;

        return $this;
    }


    /**
     * {@inheritdoc}
     */
    public function run()
    {
        try {
            foreach ($this->dirs as $dir => $keep) {
                if ($dir) {
                    $this->printTaskInfo(
                        sprintf(
                            'Cleaning directory %s while keeping %d items.',
                            $dir,
                            $keep
                        )
                    );
                    $this->cleanDir($dir, $keep);
                }
            }
        } catch (\Exception $e) {
            return \Robo\Result::fromException($this, $e);
        }
        return \Robo\Result::success($this);
    }

    /**
     * Clean a directory.
     *
     * @param string $dir
     *   The directory to clean.
     * @param int $keep
     *   The number of items to keep.
     */
    protected function cleanDir($dir, $keep)
    {
        $finder = clone $this->finder;
        $finder->in($dir);
        $finder->depth(0);
        $this->doSort($finder);
        $items = iterator_to_array($finder->getIterator());
        if ($keep) {
            array_splice($items, -$keep);
        }
        while ($items) {
            $item = reset($items);
            try {
                $this->fs->chmod($item, 0777, 0000, true);
            } catch (IOException $e) {
                // If chmod didn't work and the exception contains a path, try
                // to remove anyway.
                $path = $e->getPath();
                if ($path && realpath($path) !== realpath($item)) {
                    $this->fs->remove($path);
                    continue;
                }
            }
            $this->fs->remove($item);
            array_shift($items);
        }
    }

    /**
     * Sort the finder.
     *
     * @param Finder $finder
     *   The finder to sort.
     */
    protected function doSort(Finder $finder)
    {
        switch ($this->sort) {
            case static::SORT_NAME:
                $finder->sortByName();
                break;

            case static::SORT_TYPE:
                $finder->sortByType();
                break;

            case static::SORT_ACCESS_TIME:
                $finder->sortByAccessedTime();
                break;

            case static::SORT_MODIFIED_TIME:
                $finder->sortByModifiedTime();
                break;

            case static::SORT_CHANGED_TIME:
                $finder->sortByType();
                break;

            case $this->sort instanceof \Closure:
                $finder->sort($this->sort);
                break;
        }
    }
}