<?php

// this file will be tmeporarily placed inside the /public folder during
// deployment, it will run and then be deleted once it has finished

class Laravel_Scripts_Deployer
{
    const VENDOR = "vendor";
    const VENDOR_NEW = "vendor-new";
    const VENDOR_OLD = "vendor-old";

    function __construct()
    {
        // here the scripts steps:

        // 1: unzip the vendor.zip file uploaded by lftp, only continue if it
        // succeded
        if (self::unzip(self::VENDOR, self::VENDOR_NEW)) {
            // 2: in case there is already an existing vendor folder tmeporarily
            // rename it, first time deploy don't have it so we need this check
            if (self::folderExists(self::VENDOR)) {
                self::renameFolder(self::VENDOR, self::VENDOR_OLD);
            }

            // 3: rename the new unzipped vendor to the actual vendor folder
            self::renameFolder(self::VENDOR_NEW, self::VENDOR);

            // 4: delete the now empty unzip extracting destination
            self::deleteDir(self::getPath(self::VENDOR_NEW));

            // 5: delete the vendor.zip file
            unlink(self::getPath(self::VENDOR.'.zip'));

            // 6: delete the old vendor folder if it exists, it might take few
            // seconds
            if (self::folderExists(self::VENDOR_OLD)) {
                self::deleteDir(self::getPath(self::VENDOR_OLD));
            }

            // 7: suicide, this script auto-deletes itself
            // unlink(__FILE__);

            // exit('Updated vendor files successfully!');
        }

        // exit('Failed to unzip vendor.zip :(');
    }

    /**
     * Get path, we are in the public directory so we need to go 1 level up to
     * be in the project root
     *
     * @param string $path
     * @return string
     */
    private static function getPath($path)
    {
        return dirname(__FILE__, 2) . '/' . $path;
    }

    /**
     * Check that a folder exists
     *
     * @param string $path Relative to the project root
     * @return void
     */
    private static function folderExists($path)
    {
        return file_exists(self::getPath($path)) &&
            is_dir(self::getPath($path));
    }

    /**
     * Rename folder
     *
     * @param string $src Relative to the project root
     * @param string $dest Relative to the project root
     * @return void
     */
    private static function renameFolder($src, $dest)
    {
        rename(self::getPath($src), self::getPath($dest));
    }

    /**
     * Unzip source to given destination path (relative to the root)
     *
     * @param string $src Relative to the project root
     * @param string $dest Relative to the project root
     * @return bool
     */
    private static function unzip($src, $dest)
    {
        $zip = new ZipArchive();

        if ($zip->open(self::getPath($src . '.zip')) === true) {
            $zip->extractTo(self::getPath($dest));
            $zip->close();

            return true;
        }

        return false;
    }

    /**
     * Delete a given directory and all its content
     *
     * @see https://stackoverflow.com/a/11614201/1938970
     * @see https://stackoverflow.com/a/3349792/1938970
     * @param string $dir Pass here an absolute path
     * @return void
     */
    private static function deleteDir($dir)
    {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." && $object != "..") {
                    if (filetype($dir . "/" . $object) == "dir") {
                        self::deleteDir($dir . "/" . $object);
                    } else {
                        unlink($dir . "/" . $object);
                    }
                }
            }
            reset($objects);
            rmdir($dir);
        }
    }
}

/**
 * Crawler
 *
 * @see https://stackoverflow.com/a/24041887/1938970
 */
class Laravel_Scripts_Crawler
{
    protected $_url;
    protected $_depth;
    protected $_host;
    protected $_useHttpAuth = false;
    protected $_user;
    protected $_pass;
    protected $_seen = [];
    protected $_filter = [];

    public function __construct()
    {
        $url =
            (!empty($_SERVER['HTTPS']) ? 'https' : 'http') .
            '://' .
            $_SERVER['HTTP_HOST'] .
            '/';
        $this->_url = $url;
        $this->_depth = 5;
        $parse = parse_url($url);
        $this->_host = $parse['host'];

        if (strpos($url, '.acanto.net') !== 0) {
            $this->setHttpAuth('acanto', 'acanto');
        }

        $this->logFile = __DIR__ . '/../storage/logs/crawled.log';

        if (file_exists($this->logFile)) {
            unlink($this->logFile);
        }

        // $this->addFilterPath('customer/account/login/referer');
        $this->run();
    }

    protected function _processAnchors($content, $url, $depth)
    {
        $dom = new DOMDocument('1.0');
        @$dom->loadHTML($content);
        $anchors = $dom->getElementsByTagName('a');

        foreach ($anchors as $element) {
            $href = $element->getAttribute('href');

            // exclude anchor links!
            if (strpos($href, '#') !== 0) {
                if (strpos($href, 'http') !== 0) {
                    $path = '/' . ltrim($href, '/');
                    if (extension_loaded('http')) {
                        $href = http_build_url($url, ['path' => $path]);
                    } else {
                        $parts = parse_url($url);
                        $href = $parts['scheme'] . '://';
                        if (isset($parts['user']) && isset($parts['pass'])) {
                            $href .=
                                $parts['user'] . ':' . $parts['pass'] . '@';
                        }
                        $href .= $parts['host'];
                        if (isset($parts['port'])) {
                            $href .= ':' . $parts['port'];
                        }
                        $href .= $path;
                    }
                }
                // crawl only link that belongs to the start domain
                $this->crawlPage($href, $depth - 1);
            }
        }
    }

    protected function _getContent($url)
    {
        $handle = curl_init($url);
        if ($this->_useHttpAuth) {
            curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
            curl_setopt(
                $handle,
                CURLOPT_USERPWD,
                $this->_user . ":" . $this->_pass
            );
        }
        // follows 302 redirect, creates problem wiht authentication
        // curl_setopt($handle, CURLOPT_FOLLOWLOCATION, TRUE);
        // return the content
        curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);

        // Get the HTML or whatever is linked in $url.
        $response = curl_exec($handle);
        // response total time
        $time = curl_getinfo($handle, CURLINFO_TOTAL_TIME);
        // Check for 404 (file not found).
        $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);

        curl_close($handle);
        return [$response, $httpCode, $time];
    }

    protected function _printResult($url, $depth, $httpcode, $time)
    {
        $currentDepth = $this->_depth - $depth;
        $count = count($this->_seen);
        $content = "[$httpcode]: $url\n";
        file_put_contents($this->logFile, $content, FILE_APPEND);
    }

    protected function isValid($url, $depth)
    {
        if (
            strpos($url, $this->_host) === false ||
            $depth === 0 ||
            isset($this->_seen[$url])
        ) {
            return false;
        }
        foreach ($this->_filter as $excludePath) {
            if (strpos($url, $excludePath) !== false) {
                return false;
            }
        }
        return true;
    }

    public function crawlPage($url, $depth)
    {
        if (!$this->isValid($url, $depth)) {
            return;
        }
        // add to the seen URL
        $this->_seen[$url] = true;
        // get Content and Return Code
        list($content, $httpcode, $time) = $this->_getContent($url);
        // print Result for current Page
        $this->_printResult($url, $depth, $httpcode, $time);
        // process subPages
        $this->_processAnchors($content, $url, $depth);
    }

    public function setHttpAuth($user, $pass)
    {
        $this->_useHttpAuth = true;
        $this->_user = $user;
        $this->_pass = $pass;
    }

    public function addFilterPath($path)
    {
        $this->_filter[] = $path;
    }

    public function run()
    {
        $this->crawlPage($this->_url, $this->_depth);
    }
}

new Laravel_Scripts_Deployer();
new Laravel_Scripts_Crawler();

// this script auto-deletes itself
unlink(__FILE__);
