byjg/php-daemonize

View on GitHub
src/Daemonize.php

Summary

Maintainability
C
7 hrs
Test Coverage
<?php

namespace ByJG\Daemon;

class Daemonize
{
    protected static $writer = null;

    public static function setWriter(?ServiceWriter $writer)
    {
        self::$writer = $writer;
    }

    public static function getWriter()
    {
        if (self::$writer == null) {
            self::$writer = new ServiceWriter();
        }

        return self::$writer;
    }

    public static function  install(
        $svcName,
        $className,
        $bootstrap,
        $curdir,
        $template,
        $description,
        $consoleArgs,
        $environment,
        $check = true
    )
    {
        $targetPathAvailable = [
            'initd' => "/etc/init.d/$svcName",
            'crond' => "/etc/cron.d/$svcName",
            'upstart' => "/etc/init/$svcName.conf",
            'systemd' => "/etc/systemd/system/$svcName.service"
        ];
        if (!isset($targetPathAvailable[$template])) {
            throw new \Exception(
                "Template $template does not exists. Available templates are: "
                . implode(',', array_keys($targetPathAvailable))
            );
        }

        $targetServicePath = $targetPathAvailable[$template];
        $templatePath = __DIR__ . "/../template/linux-" . $template . "-service.tpl";

        if (!file_exists($templatePath)) {
            throw new \Exception("Template '$templatePath' not found");
        }

        if (!file_exists(realpath($curdir)) && $check) {
            throw new \Exception("RootPath '" . $curdir . "' not found. Use an absolute path. e.g. /projects/example");
        }

        if (!file_exists($curdir . '/' . $bootstrap) && $check) {
            throw new \Exception("Bootstrap '$bootstrap' not found. Use a relative path from root directory. e.g. vendor/autoload.php");
        }

        $autoload = realpath(__DIR__ . "/../vendor/autoload.php");
        if (!file_exists($autoload)) {
            $autoload = realpath(__DIR__ . "/../../../autoload.php");
            if (!file_exists($autoload) && $check) {
                throw new \Exception('Daemonize autoload not found. Did you run `composer dump-autload`?');
            }
        }

        $consoleArgsPrepared = '';
        if (!empty($consoleArgs)) {
            $consoleArgsPrepared = '--http-get ' . http_build_query($consoleArgs, '', ' --http-get ');
        }

        $environmentPrepared = '/etc/daemonize/' . $svcName . '.env';

        $serviceTemplatePath = __DIR__ . "/../template/_service.php.tpl";
        $daemonizeService = realpath(__DIR__ . "/../scripts/daemonize");

        $vars = [
            '#DESCRIPTION#' => $description,
            '#DAEMONBOOTSTRAP#' => $autoload,
            '#CLASS#' => str_replace("\\", "\\\\", $className),
            '#BOOTSTRAP#' => $bootstrap,
            '#SVCNAME#' => $svcName,
            '#ROOTPATH#' => realpath($curdir),
            '#CONSOLEARGS#' => $consoleArgsPrepared,
            '#ENVIRONMENT#' => $environmentPrepared,
            '#PHPPATH#' => PHP_BINARY,
            '#SERVICETEMPLATEPATH#' => $serviceTemplatePath,
            '#DAEMONIZESERVICE#' => $daemonizeService,
            "#ENVCMDLINE#" => implode(
                ' ',
                array_map(
                    function ($v, $k) {
                        return "$k=\"$v\"";
                    },
                    $environment,
                    array_keys($environment)
                )
            )
        ];

        $templateStr = Daemonize::replaceVars($vars, file_get_contents($templatePath));

        // Check if is OK
        if ($check) {
            require_once($vars['#BOOTSTRAP#']);
            $classParts = explode('::', str_replace("\\\\", "\\", $vars['#CLASS#']));
            if (!class_exists($classParts[0])) {
                throw new \Exception('Could not find class ' . $classParts[0]);
            }
            $className = $classParts[0];
            $classTest = new $className();
            if (!method_exists($classTest, $classParts[1])) {
                throw new \Exception('Could not find method ' . $vars['#CLASS#']);
            }
        }

        Daemonize::getWriter()->writeEnvironment($environmentPrepared, $environment);
        Daemonize::getWriter()->writeService($targetServicePath, $templateStr, $template == 'initd' ? 0755 : null);

        return true;
    }

    protected static function replaceVars($vars, $text)
    {
        foreach ($vars as $searchFor=>$replace) {
            $text = str_replace($searchFor, $replace, $text);
        }
        return $text;
    }

    public static function uninstall($svcName)
    {
        $list = [
            "/etc/init.d/$svcName",
            "/etc/init/$svcName.conf",
            "/etc/systemd/system/$svcName.service",
            '/etc/daemonize/' . $svcName . '.env',
            '/etc/cron.d/' . $svcName,
        ];

        $found = false;
        foreach ($list as $service) {
            if (file_exists($service)) {
                $found = true;
                if (strpos($service, ".env") === false && !self::isDaemonizeService($service)) {
                    throw new DaemonizeException("Service '$svcName' was not created by PHP Daemonize");
                }
                unlink($service);
            }
        }

        if (!$found) {
            throw new DaemonizeException("Service '$svcName' does not exists");
        }

        restore_error_handler();
    }

    protected static function isDaemonizeService($filename)
    {
        set_error_handler(function ($number, $error) {
            throw new \Exception($error);
        });

        if (!file_exists($filename)) {
            return false;
        }
        $contents = file_get_contents($filename);

        return (strpos($contents, 'PHP_DAEMONIZE') !== false);
    }

    public static function listServices()
    {
        $list = [
            "initd" => glob("/etc/init.d/*"),
            "crond" => glob("/etc/cron.d/*"),
            "upstart" => glob("/etc/init/*.conf"),
            "systemd" => glob("/etc/systemd/system/*.service")
        ];
        $return = [];

        foreach ($list as $svcType => $filenames) {
            foreach ($filenames as $filename) {
                if (self::isDaemonizeService($filename)) {
                    $return[] = $svcType . ": " . 
                        str_replace(
                        '.service',
                        '',
                        str_replace(
                            '.conf',
                            '',
                            basename($filename)
                        )
                    );
                }
            }
        }

        return $return;
    }

}