webworld888/app/Console/Commands/ComponentMakeCommand.php
2021-10-26 19:14:12 +09:00

315 lines
9.0 KiB
PHP

<?php
/**
* ComponentMakeCommand.php
*
* PHP version 7
*
* @category Commands
* @package App\Console\Commands
* @author XE Developers <developers@xpressengine.com>
* @copyright 2020 Copyright XEHub Corp. <https://www.xehub.io>
* @license http://www.gnu.org/licenses/lgpl-3.0-standalone.html LGPL
* @link https://xpressengine.io
*/
namespace App\Console\Commands;
use Illuminate\Filesystem\Filesystem;
use ReflectionClass;
use Symfony\Component\Console\Input\InputOption;
use Xpressengine\Foundation\Operator;
use Xpressengine\Plugin\PluginEntity;
use Xpressengine\Plugin\PluginHandler;
use Xpressengine\Plugin\PluginProvider;
/**
* Abstract Class ComponentMakeCommand
*
* @category Commands
* @package App\Console\Commands
* @author XE Developers <developers@xpressengine.com>
* @copyright 2020 Copyright XEHub Corp. <https://www.xehub.io>
* @license http://www.gnu.org/licenses/lgpl-3.0-standalone.html LGPL
* @link https://xpressengine.io
*/
abstract class ComponentMakeCommand extends MakeCommand
{
/**
* @var \stdClass|null
*/
protected $originComposerData = null;
/**
* @var string
*/
protected $componentType;
/**
* Create a new component creator command instance.
*
* @param Filesystem $files Filesystem instance
* @param Operator $operator Operator instance
* @param PluginHandler $handler PluginHandler instance
* @param PluginProvider $provider PluginProvider instance
*/
public function __construct(Filesystem $files, Operator $operator, PluginHandler $handler, PluginProvider $provider)
{
parent::__construct($files, $operator, $handler, $provider);
$this->addOption('--title', '', InputOption::VALUE_OPTIONAL, 'The title of component');
$this->addOption('--description', '', InputOption::VALUE_OPTIONAL, 'The description of component');
}
/**
* GetTargetPlugin
*
* @return PluginEntity
* @throws \Exception
*/
protected function getPlugin()
{
if(!$plugin = $this->handler->getPlugin($name = $this->getPluginName())) {
throw new \Exception("Unable to find a plugin to locate the skin file. plugin[$name] is not found.");
}
return $plugin;
}
/**
* Get plugin name
*
* @return string
*/
abstract protected function getPluginName();
/**
* Get path to be create
*
* @param string $path given path
* @return string
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function getPath($path = null)
{
if (!$path) {
$path = $this->getDefaultPath();
$autoload = $this->getOriginComposerData('autoload');
$prefix = array_first((array)($autoload->{'psr-4'} ?? []));
$path = $prefix ? rtrim($prefix,'/') . '/' . $path : $path;
}
return $path;
}
/**
* Get default path for component
*
* @return string
*/
abstract protected function getDefaultPath();
/**
* Get original composer data
*
* @param string|null $key key for data
* @return mixed|null
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws \Exception
*/
protected function getOriginComposerData($key = null)
{
if (!$this->originComposerData) {
$composerStr = $this->files->get($this->getPlugin()->getPath('composer.json'));
$this->originComposerData = json_decode($composerStr);
}
return $key ? data_get($this->originComposerData, $key) : $this->originComposerData;
}
/**
* Get namespace
*
* @param string $path given path
* @return string
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws \ReflectionException
* @throws \Exception
*/
protected function getNamespace($path)
{
$namespace = null;
$autoload = $this->getOriginComposerData('autoload');
foreach ((array)($autoload->{'psr-4'} ?? []) as $ns => $head) {
if (starts_with($path, $head)) {
$namespace = $ns;
$path = substr($path, strlen($head));
break;
}
}
if (!$namespace) {
$pluginClass = new ReflectionClass($this->getPlugin()->getClass());
$namespace = $pluginClass->getNamespaceName();
}
$segments = array_map(function ($segment) {
return studly_case($segment);
}, explode('/', $path));
return rtrim($namespace, '\\') . '\\' . implode('\\', $segments);
}
/**
* Get class file
*
* @param string $path given path
* @param string $className class name
* @return array|string
* @throws \Exception
*/
protected function getClassFile($path, $className)
{
$path = $path."/$className.php";
if(file_exists($this->getPlugin()->getPath($path))) {
throw new \Exception("file[$path] already exists.");
}
return $path;
}
/**
* Get title
*
* @return string
*/
protected function getTitleInput()
{
return $this->option('title') ?: studly_case($this->getComponentName()) . ' ' . $this->componentType;
}
/**
* Get component name
*
* @return string
*/
abstract protected function getComponentName();
/**
* Get component description
*
* @return string
* @throws \Exception
*/
protected function getDescriptionInput()
{
if(!$description = $this->option('description')) {
$description = 'The '.$this->componentType.' supported by '.ucfirst($this->getPlugin()->getId()).' plugin.';
}
return $description;
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStubFile($filename)
{
return $this->getStubPath().'/'.$filename;
}
/**
* Register component
*
* @param PluginEntity $plugin plugin entity
* @param string $id component id
* @param string $class class name
* @param string $file class file path
* @param array $info component information
* @return int
* @throws \Exception
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function registerComponent(PluginEntity $plugin, $id, $class, $file, $info = [])
{
$data = unserialize(serialize($this->getOriginComposerData())); // clone
$component = data_get($data, 'extra.xpressengine.component');
if(isset($component->$id)) {
throw new \Exception(sprintf('component[%s] already exists.', $id));
}
$component->$id = (object)array_merge($info, ['class' => $class]);
data_set($data, 'extra.xpressengine.component', $component);
if (!$this->existsAutoload($class, $file)) {
// add autoload
$classmap = data_get($data, 'autoload.classmap', []);
$classmap[] = $file;
data_set($data, 'autoload.classmap', $classmap);
}
return $this->files->put($plugin->getPath('composer.json'), json_format(json_encode($data)));
}
/**
* Determine if need to register autoload given class and file
*
* @param string $class class name
* @param string $file class file path
* @return bool
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function existsAutoload($class, $file)
{
$autoload = $this->getOriginComposerData('autoload');
foreach ((array)($autoload->{'psr-4'} ?? []) as $ns => $head) {
if (starts_with($file, $head) && starts_with($class, $ns)) {
return true;
}
}
return in_array($file, (array)($autoload->classmap ?? []));
}
/**
* Refresh the given plugin
*
* @param PluginEntity $plugin plugin entity
* @return int
* @throws \Exception
*/
protected function refresh(PluginEntity $plugin)
{
$result = $this->composerDump($plugin->hasVendor() ? $plugin->getPath() : base_path());
if (0 !== $result) {
throw new \Exception('Fail to run composer dump');
}
return $result;
}
/**
* Clean
*
* @param string $path path in plugin
* @return void
* @throws \Exception
*/
protected function clean($path)
{
$plugin = $this->getPlugin();
// delete path
if(is_writable($plugin->getPath($path))) {
$this->files->deleteDirectory($plugin->getPath($path));
}
// unregister component from composer.json
$composerFile = $plugin->getPath('composer.json');
if($this->originComposerData !== null && is_writable($composerFile)) {
$this->files->put($composerFile, json_format(json_encode($this->originComposerData)));
}
}
}