CLI-Component/src/FuzeWorks/CLI/CommandProcessor.php

193 lines
6.4 KiB
PHP

<?php
/**
* FuzeWorks CLIComponent
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2021 i15
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author i15
* @copyright Copyright (c) 2013 - 2021, i15. (https://i15.nl)
* @license https://opensource.org/licenses/MIT MIT License
*
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\CLI;
use FuzeWorks\Event\RouterCallViewEvent;
use FuzeWorks\Events;
use FuzeWorks\Exception\HaltException;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Exception\RouterException;
use FuzeWorks\Factory;
use FuzeWorks\Logger;
use FuzeWorks\Priority;
use FuzeWorks\Router;
class CommandProcessor
{
protected Router $router;
protected Output $output;
protected array $options;
public function routerCallViewEventListener(RouterCallViewEvent $event)
{
// If this is related to CLI, add the options as a parameter
if ($event->view instanceof CLIView)
$event->addParameter($this->options);
}
/**
* Executes a command in the CLI environment.
*
* Returns true on successful processing. Returns false on failure
*
* @param string $commandString
*/
public function executeCommand(string $commandString): void
{
/** @var Router $router */
/** @var Output $output */
$this->router = Factory::getInstance('router');
$this->output = Factory::getInstance('cliOutput');
// Pre-process the command
$commandString = $this->preProcess($commandString);
// First test if the command is empty. If it is, cancel execution
if (empty($commandString)) {
$this->output->warningOut("No command provided!");
return;
}
// Distill different parts of the command
$parts = explode(' ', $commandString);
// Prepare the logCount for a consolidation of logs later
// @todo Find a better way to determine to first log. LogCount is not effective and contains many logs from router
// @todo Perhaps using routerCallViewEvent would be useful. Much closer than this early logCount
$logCount = count(Logger::$logs);
try {
$this->router->route($commandString, 'cli');
} catch (NotFoundException $e) {
$this->output->warningOut("Requested command '$parts[0]' was not found.");
} catch (RouterException $e) {
$this->output->errorOut($e->getMessage());
} catch (HaltException $e) {
$this->output->errorOut("Requested command '$parts[0]' was denied.");
}
// Consolidate logs
/** @var array $logs */
$logs = array_slice(Logger::$logs, $logCount);
// And parse them
$verbose = isset($this->options['v']) || isset($this->options['verbose']);
$this->parseLogs($logs, $verbose);
}
/**
* Processes a commandString and extracts options from it
*
* @todo Proper compatiblity with string like '--option "Hello World"'
*
* @param string $commandString
* @return string
*/
protected function preProcess(string $commandString): string
{
// Split up the command into parts
$parts = explode(' ', $commandString);
// Pass over each part and look for special recognized parts, such as options
$options = [];
$keepParts = [];
for ($i=0;$i<count($parts);$i++)
{
// First test for longOption
if (substr($parts[$i], 0, 2) === '--')
{
$longOptKey = substr($parts[$i], 2);
// If the longOpt contains an '=' sign, split up the string there and use parts as key and val
if (strpos($longOptKey, '=') !== false)
{
$longEx = explode('=', $longOptKey);
$longOptKey = $longEx[0];
$longOptVal = $longEx[1];
$options[$longOptKey] = $longOptVal;
} else {
$options[$longOptKey] = true;
}
}
// Then test for shortOption
elseif (substr($parts[$i], 0, 1) === '-')
{
$letters = substr($parts[$i], 1);
$ops = str_split($letters, 1);
foreach ($ops as $letter)
$options[$letter] = true;
}
// If neither, add it to the keep list
else
$keepParts[] = $parts[$i];
}
$this->options = $options;
return implode(' ', $keepParts);
}
/**
* Parse all logs from FuzeWorks::logger and output all that have been thrown for this command
*
* @param array $logs
* @param bool $verbose
*/
protected function parseLogs(array $logs, bool $verbose = false)
{
foreach ($logs as $log) {
switch ($log['type']) {
case 'WARNING':
$this->output->warningOut($log['message']);
break;
case 'ERROR':
case 'EXCEPTION':
$this->output->errorOut($log['message']);
break;
case 'LEVEL_START':
case 'INFO':
if ($verbose)
$this->output->lineOut('&f[&rINFO&f]&r ' . $log['message']);
break;
}
}
}
}