<commit_before><?php

class CM_Cli_CommandManager {

    const TIMEOUT = 600;

    /** @var CM_Cli_Command[]|null */
    private $_commands = null;

    /** @var int */
    private $_forks = null;

    /** @var CM_InputStream_Interface */
    private $_streamInput;

    /** @var CM_OutputStream_Interface */
    private $_streamOutput;

    /** @var CM_OutputStream_Interface */
    private $_streamError;

    public function __construct() {
        $this->_commands = array();
        $this->_setStreamInput(new CM_InputStream_Readline());
        $this->_setStreamOutput(new CM_OutputStream_Stream_StandardOutput());
        $this->_setStreamError(new CM_OutputStream_Stream_StandardError());
    }

    /**
     * @param string $className
     * @throws CM_Exception_Invalid
     */
    public function addRunnable($className) {
        $class = new ReflectionClass($className);
        if (!$class->isSubclassOf('CM_Cli_Runnable_Abstract')) {
            throw new CM_Exception_Invalid('Can only add subclasses of `CM_Cli_Runnable_Abstract`');
        }
        if ($class->isAbstract()) {
            throw new CM_Exception_Invalid('Cannot add abstract runnable');
        }
        foreach ($class->getMethods() as $method) {
            if (!$method->isConstructor() && $method->isPublic() && !$method->isStatic()) {
                $command = new CM_Cli_Command($method, $class);
                $this->_commands[$command->getName()] = $command;
            }
        }
    }

    public function autoloadCommands() {
        $classes = CM_Util::getClassChildren('CM_Cli_Runnable_Abstract', false);
        foreach ($classes as $className) {
            $this->addRunnable($className);
        }
    }

    /**
     * @param boolean|null $quiet
     * @param boolean|null $quietWarnings
     * @param boolean|null $nonInteractive
     * @param int|null     $forks
     */
    public function configure($quiet = null, $quietWarnings = null, $nonInteractive = null, $forks = null) {
        $forks = (int) $forks;
        if ($quiet) {
            $this->_setStreamOutput(new CM_OutputStream_Null());
        }
        if ($quietWarnings) {
            CM_Bootloader::getInstance()->getExceptionHandler()->setPrintSeverityMin(CM_Exception::ERROR);
        }
        if ($nonInteractive) {
            $this->_setStreamInput(new CM_InputStream_Null());
        }
        if ($forks > 1) {
            $this->_forks = $forks;
        }
    }

    /**
     * @return CM_Cli_Command[]
     */
    public function getCommands() {
        $commands = $this->_commands;
        ksort($commands);
        return $commands;
    }

    /**
     * @param string|null $packageName
     * @throws CM_Cli_Exception_InvalidArguments
     * @return string
     */
    public function getHelp($packageName = null) {
        $helpHeader = '';
        $helpHeader .= 'Usage:' . PHP_EOL;
        $helpHeader .= ' [options] <command> [arguments]' . PHP_EOL;
        $helpHeader .= PHP_EOL;
        $helpHeader .= 'Options:' . PHP_EOL;
        $reflectionMethod = new ReflectionMethod($this, 'configure');
        foreach (CM_Cli_Arguments::getNamedForMethod($reflectionMethod) as $paramString) {
            $helpHeader .= ' ' . $paramString . PHP_EOL;
        }
        $helpHeader .= PHP_EOL;
        $helpHeader .= 'Commands:' . PHP_EOL;
        $help = '';
        $commands = $this->getCommands();
        foreach ($commands as $command) {
            if (!$command->isAbstract() && (!$packageName || $packageName === $command->getPackageName())) {
                $help .= ' ' . $command->getHelp() . PHP_EOL;
            }
        }
        if ($packageName && !$help) {
            throw new CM_Cli_Exception_InvalidArguments('Package `' . $packageName . '` not found.');
        }
        return $helpHeader . $help;
    }

    /**
     * @param CM_Cli_Arguments $arguments
     * @throws CM_Exception
     * @return int
     */
    public function run(CM_Cli_Arguments $arguments) {
        $method = new ReflectionMethod($this, 'configure');
        $parameters = $arguments->extractMethodParameters($method);
        $method->invokeArgs($this, $parameters);
        try {
            $packageName = $arguments->getNumeric()->shift();
            $methodName = $arguments->getNumeric()->shift();

            if (!$packageName) {
                $this->_outputError($this->getHelp());
                return 1;
            }
            if (!$methodName) {
                $this->_outputError($this->getHelp($packageName));
                return 1;
            }
            $command = $this->_getCommand($packageName, $methodName);

            if ($command->getSynchronized()) {
                $this->_checkLock($command);
                $this->_lockCommand($command);
                $commandManager = $this;
                $terminationCallback = function () use ($commandManager, $command) {
                    $commandManager->unlockCommand($command);
                };
            } else {
                $terminationCallback = null;
            }

            $transactionName = 'cm ' . $packageName . ' ' . $methodName;
            $streamInput = $this->_streamInput;
            $streamOutput = $this->_streamOutput;
            $workload = function () use ($transactionName, $command, $arguments, $streamInput, $streamOutput) {
                CMService_Newrelic::getInstance()->startTransaction($transactionName);
                $command->run($arguments, $streamInput, $streamOutput);
            };

            $forks = max($this->_forks, 1);
            $process = $this->_getProcess();
            for ($i = 0; $i < $forks; $i++) {
                $process->fork($workload);
            }
            $process->waitForChildren($command->getKeepalive(), $terminationCallback);
            return 0;
        } catch (CM_Cli_Exception_InvalidArguments $e) {
            $this->_outputError('ERROR: ' . $e->getMessage() . PHP_EOL);
            if (isset($command)) {
                $this->_outputError('Usage: ' . $arguments->getScriptName() . ' ' . $command->getHelp());
            } else {
                $this->_outputError($this->getHelp());
            }
            return 1;
        } catch (CM_Cli_Exception_Internal $e) {
            $this->_outputError('ERROR: ' . $e->getMessage() . PHP_EOL);
            return 1;
        }
    }

    public static function monitorSynchronizedCommands() {
        $time = time();
        $timeoutStamp = $time + self::TIMEOUT;
        $hostId = CM_Process::getInstance()->getHostId();
        $result = CM_Db_Db::select('cm_cli_command_manager_process', array('commandName', 'processId'), array('hostId' => $hostId));
        foreach ($result->fetchAll() as $row) {
            $commandName = $row['commandName'];
            $processId = (int) $row['processId'];
            if (CM_Process::isRunning($processId)) {
                CM_Db_Db::update('cm_cli_command_manager_process', array('timeoutStamp' => $timeoutStamp), array('commandName' => $commandName));
            }
        }
        CM_Db_Db::delete('cm_cli_command_manager_process', '`timeoutStamp` < ' . $time);
    }

    /**
     * @param CM_Cli_Command $command
     */
    public function unlockCommand(CM_Cli_Command $command) {
        $commandName = $command->getName();<commit_msg>Removed command locks if not running<commit_after><?php

class CM_Cli_CommandManager {

    const TIMEOUT = 600;

    /** @var CM_Cli_Command[]|null */
    private $_commands = null;

    /** @var int */
    private $_forks = null;

    /** @var CM_InputStream_Interface */
    private $_streamInput;

    /** @var CM_OutputStream_Interface */
    private $_streamOutput;

    /** @var CM_OutputStream_Interface */
    private $_streamError;

    public function __construct() {
        $this->_commands = array();
        $this->_setStreamInput(new CM_InputStream_Readline());
        $this->_setStreamOutput(new CM_OutputStream_Stream_StandardOutput());
        $this->_setStreamError(new CM_OutputStream_Stream_StandardError());
    }

    /**
     * @param string $className
     * @throws CM_Exception_Invalid
     */
    public function addRunnable($className) {
        $class = new ReflectionClass($className);
        if (!$class->isSubclassOf('CM_Cli_Runnable_Abstract')) {
            throw new CM_Exception_Invalid('Can only add subclasses of `CM_Cli_Runnable_Abstract`');
        }
        if ($class->isAbstract()) {
            throw new CM_Exception_Invalid('Cannot add abstract runnable');
        }
        foreach ($class->getMethods() as $method) {
            if (!$method->isConstructor() && $method->isPublic() && !$method->isStatic()) {
                $command = new CM_Cli_Command($method, $class);
                $this->_commands[$command->getName()] = $command;
            }
        }
    }

    public function autoloadCommands() {
        $classes = CM_Util::getClassChildren('CM_Cli_Runnable_Abstract', false);
        foreach ($classes as $className) {
            $this->addRunnable($className);
        }
    }

    /**
     * @param boolean|null $quiet
     * @param boolean|null $quietWarnings
     * @param boolean|null $nonInteractive
     * @param int|null     $forks
     */
    public function configure($quiet = null, $quietWarnings = null, $nonInteractive = null, $forks = null) {
        $forks = (int) $forks;
        if ($quiet) {
            $this->_setStreamOutput(new CM_OutputStream_Null());
        }
        if ($quietWarnings) {
            CM_Bootloader::getInstance()->getExceptionHandler()->setPrintSeverityMin(CM_Exception::ERROR);
        }
        if ($nonInteractive) {
            $this->_setStreamInput(new CM_InputStream_Null());
        }
        if ($forks > 1) {
            $this->_forks = $forks;
        }
    }

    /**
     * @return CM_Cli_Command[]
     */
    public function getCommands() {
        $commands = $this->_commands;
        ksort($commands);
        return $commands;
    }

    /**
     * @param string|null $packageName
     * @throws CM_Cli_Exception_InvalidArguments
     * @return string
     */
    public function getHelp($packageName = null) {
        $helpHeader = '';
        $helpHeader .= 'Usage:' . PHP_EOL;
        $helpHeader .= ' [options] <command> [arguments]' . PHP_EOL;
        $helpHeader .= PHP_EOL;
        $helpHeader .= 'Options:' . PHP_EOL;
        $reflectionMethod = new ReflectionMethod($this, 'configure');
        foreach (CM_Cli_Arguments::getNamedForMethod($reflectionMethod) as $paramString) {
            $helpHeader .= ' ' . $paramString . PHP_EOL;
        }
        $helpHeader .= PHP_EOL;
        $helpHeader .= 'Commands:' . PHP_EOL;
        $help = '';
        $commands = $this->getCommands();
        foreach ($commands as $command) {
            if (!$command->isAbstract() && (!$packageName || $packageName === $command->getPackageName())) {
                $help .= ' ' . $command->getHelp() . PHP_EOL;
            }
        }
        if ($packageName && !$help) {
            throw new CM_Cli_Exception_InvalidArguments('Package `' . $packageName . '` not found.');
        }
        return $helpHeader . $help;
    }

    /**
     * @param CM_Cli_Arguments $arguments
     * @throws CM_Exception
     * @return int
     */
    public function run(CM_Cli_Arguments $arguments) {
        $method = new ReflectionMethod($this, 'configure');
        $parameters = $arguments->extractMethodParameters($method);
        $method->invokeArgs($this, $parameters);
        try {
            $packageName = $arguments->getNumeric()->shift();
            $methodName = $arguments->getNumeric()->shift();

            if (!$packageName) {
                $this->_outputError($this->getHelp());
                return 1;
            }
            if (!$methodName) {
                $this->_outputError($this->getHelp($packageName));
                return 1;
            }
            $command = $this->_getCommand($packageName, $methodName);

            if ($command->getSynchronized()) {
                $this->monitorSynchronizedCommands();
                $this->_checkLock($command);
                $this->_lockCommand($command);
                $commandManager = $this;
                $terminationCallback = function () use ($commandManager, $command) {
                    $commandManager->unlockCommand($command);
                };
            } else {
                $terminationCallback = null;
            }

            $transactionName = 'cm ' . $packageName . ' ' . $methodName;
            $streamInput = $this->_streamInput;
            $streamOutput = $this->_streamOutput;
            $workload = function () use ($transactionName, $command, $arguments, $streamInput, $streamOutput) {
                CMService_Newrelic::getInstance()->startTransaction($transactionName);
                $command->run($arguments, $streamInput, $streamOutput);
            };

            $forks = max($this->_forks, 1);
            $process = $this->_getProcess();
            for ($i = 0; $i < $forks; $i++) {
                $process->fork($workload);
            }
            $process->waitForChildren($command->getKeepalive(), $terminationCallback);
            return 0;
        } catch (CM_Cli_Exception_InvalidArguments $e) {
            $this->_outputError('ERROR: ' . $e->getMessage() . PHP_EOL);
            if (isset($command)) {
                $this->_outputError('Usage: ' . $arguments->getScriptName() . ' ' . $command->getHelp());
            } else {
                $this->_outputError($this->getHelp());
            }
            return 1;
        } catch (CM_Cli_Exception_Internal $e) {
            $this->_outputError('ERROR: ' . $e->getMessage() . PHP_EOL);
            return 1;
        }
    }

    public static function monitorSynchronizedCommands() {
        $time = time();
        $timeoutStamp = $time + self::TIMEOUT;
        $hostId = CM_Process::getInstance()->getHostId();
        $result = CM_Db_Db::select('cm_cli_command_manager_process', array('commandName', 'processId'), array('hostId' => $hostId));
        foreach ($result->fetchAll() as $row) {
            $commandName = $row['commandName'];
            $processId = (int) $row['processId'];
            if (CM_Process::isRunning($processId)) {
                CM_Db_Db::update('cm_cli_command_manager_process', array('timeoutStamp' => $timeoutStamp), array('commandName' => $commandName));
            } else {
                CM_Db_Db::delete('cm_cli_command_manager_process', array('commandName' => $commandName));
            }
        }
        CM_Db_Db::delete('cm_cli_command_manager_process', '`timeoutStamp` < ' . $time);
    }

    /**
     * @param CM_Cli_Command $command
     */
    public function unlockCommand(CM_Cli_Command $command) {
        $commandName = $command->getName();
{'fraction_non_alphanumeric': 0.14317477428917935, 'fraction_numerical': 0.0014822800161703275, 'mean_word_length': 2.2134661182074042, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3497564', 'n_tokens_mistral': 4199, 'n_tokens_neox': 4053, 'n_words': 745}