212 lines
6.9 KiB
PHP
212 lines
6.9 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, [3 =>
|
||
|
[
|
||
|
'(?P<viewName>.*?)(| (?P<viewMethod>.*?)(| (?P<viewParameters>.*?)))' => ['viewType' => '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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The callable that is used to pick the correct view and controller
|
||
|
*
|
||
|
* @internal
|
||
|
* @param array $matches
|
||
|
* @param array $routeData
|
||
|
* @param string $route
|
||
|
*/
|
||
|
public function cliCallable(array $matches, array $routeData, string $route)
|
||
|
{
|
||
|
dump($this->options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|