413 lines
10 KiB
PHP
413 lines
10 KiB
PHP
<?php
|
|
|
|
namespace Dipper\Console;
|
|
|
|
use Exception;
|
|
use Illuminate\Support\Str;
|
|
use InvalidArgumentException;
|
|
use Dipper\Console\Components\Job;
|
|
use Illuminate\Support\Collection;
|
|
use Dipper\Console\Components\Domain;
|
|
use Dipper\Console\Components\Service;
|
|
use Symfony\Component\Finder\Finder as SymfonyFinder;
|
|
|
|
/**
|
|
* @author HollyTeng <n.haoyuan@gmail.com>
|
|
*/
|
|
trait Finder
|
|
{
|
|
/**
|
|
* The name of the source directory.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $srcDirectoryName = 'app';
|
|
|
|
/**
|
|
* Get the base path of the Laravel installation.
|
|
*
|
|
* @param string $path Optionally, a path to append to the base path
|
|
* @return string
|
|
*/
|
|
public function basePath($path = '')
|
|
{
|
|
return DIPPER_BASE_PATH.($path ? DIRECTORY_SEPARATOR.$path : $path);
|
|
}
|
|
|
|
/**
|
|
* Get the source directory name.
|
|
* In a microservice installation this will be `app`. `src` otherwise.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSourceDirectoryName()
|
|
{
|
|
if (file_exists($this->basePath($this->srcDirectoryName))) {
|
|
return $this->srcDirectoryName;
|
|
}
|
|
|
|
return 'app';
|
|
}
|
|
|
|
/**
|
|
* Get the namespace used for the application.
|
|
*
|
|
* @throws \Exception
|
|
* @return string
|
|
*/
|
|
public function findRootNamespace()
|
|
{
|
|
// read composer.json file contents to determine the namespace
|
|
$composer = json_decode(file_get_contents($this->basePath('composer.json')), true);
|
|
|
|
// see which one refers to the "app/" directory
|
|
foreach ($composer['autoload']['psr-4'] as $namespace => $directory) {
|
|
if ($directory === $this->getSourceDirectoryName() . '/') {
|
|
return trim($namespace, '\\');
|
|
}
|
|
}
|
|
|
|
throw new Exception('App namespace not set in composer.json');
|
|
}
|
|
|
|
/**
|
|
* Find the namespace of the foundation.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findFoundationNamespace()
|
|
{
|
|
return 'Dipper\Foundation';
|
|
}
|
|
|
|
/**
|
|
* get the root of the source directory.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findSourceRoot()
|
|
{
|
|
return $this->basePath($this->srcDirectoryName);
|
|
}
|
|
|
|
/**
|
|
* Get the path to the Composer.json file.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getComposerPath()
|
|
{
|
|
return $this->basePath('composer.json');
|
|
}
|
|
|
|
/**
|
|
* Get the path to the given configuration file.
|
|
*
|
|
* @param string $name
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getConfigPath($name)
|
|
{
|
|
return base_path('config/' . $name . '.php');
|
|
}
|
|
|
|
/**
|
|
* 解析输入的数据,适应文件夹深度.
|
|
*
|
|
* @param string $name
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function prepareNamespace($name)
|
|
{
|
|
if (strpos($name, '/')) {
|
|
$arr = explode('/', $name);
|
|
array_pop($arr);
|
|
|
|
foreach ($arr as $value) {
|
|
$this->lastNamespace .= '\\' . $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the full namespace for a given class, without the class name.
|
|
*
|
|
* @param string $name
|
|
* @return string
|
|
*/
|
|
protected function getNamespace($name)
|
|
{
|
|
return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
|
|
}
|
|
|
|
/**
|
|
* Find the root path of domains.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findDomainsRootPath()
|
|
{
|
|
return $this->findSourceRoot() . '/Domains';
|
|
}
|
|
|
|
/**
|
|
* Find the path for the given domain.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findDomainPath($domain)
|
|
{
|
|
return (!$domain) ? $this->findSourceRoot() : $this->findDomainsRootPath() . "/$domain";
|
|
}
|
|
|
|
/**
|
|
* Get the list of domains.
|
|
*
|
|
* @return \Illuminate\Support\Collection;
|
|
*/
|
|
public function listDomains()
|
|
{
|
|
$finder = new SymfonyFinder();
|
|
$directories = $finder
|
|
->depth(0)
|
|
->in($this->findDomainsRootPath())
|
|
->directories();
|
|
|
|
$domains = new Collection();
|
|
foreach ($directories as $directory) {
|
|
$name = $directory->getRelativePathName();
|
|
|
|
$domain = new Domain(
|
|
$this->realName($name),
|
|
$this->findDomainNamespace($name),
|
|
$directory->getRealPath(),
|
|
$this->relativeFromReal($directory->getRealPath())
|
|
);
|
|
|
|
$domains->push($domain);
|
|
}
|
|
|
|
return $domains;
|
|
}
|
|
|
|
/**
|
|
* Find the namespace for the given domain.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findDomainNamespace($domain)
|
|
{
|
|
$root = $this->findRootNamespace();
|
|
|
|
return (!$domain) ? $root : "$root\\Domains\\$domain";
|
|
}
|
|
|
|
/**
|
|
* Get the path to the tests of the given domain.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findDomainTestsNamespace($domain)
|
|
{
|
|
return $this->findDomainNamespace($domain) . '\\' . 'Tests';
|
|
}
|
|
|
|
/**
|
|
* Get the path to the tests of the given domain.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return string
|
|
*/
|
|
public function findDomainTestsPath($domain)
|
|
{
|
|
return $this->findDomainPath($domain) . DIRECTORY_SEPARATOR . 'Tests';
|
|
}
|
|
|
|
/**
|
|
* Find the domain for the given domain name.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return \Dipper\Console\Components\Domain
|
|
*/
|
|
public function findDomain($domain)
|
|
{
|
|
$finder = new SymfonyFinder();
|
|
$dirs = $finder->name($domain)->in($this->findDomainsRootPath())->directories();
|
|
if ($dirs->count() < 1) {
|
|
throw new Exception('Domain "' . $domain . '" could not be found.');
|
|
}
|
|
|
|
foreach ($dirs as $dir) {
|
|
$path = $dir->getRealPath();
|
|
|
|
return new Domain(
|
|
Str::studly($domain),
|
|
$this->findDomainNamespace($domain),
|
|
$path,
|
|
$this->relativeFromReal($path)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List the jobs per domain,
|
|
* optionally provide a domain name to list its jobs.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function listJobs($domainName = null)
|
|
{
|
|
$domains = ($domainName) ? [$this->findDomain(Str::studly($domainName))] : $this->listDomains();
|
|
|
|
$jobs = new Collection();
|
|
foreach ($domains as $domain) {
|
|
$jobs[$domain->name] = new Collection();
|
|
$finder = new SymfonyFinder();
|
|
|
|
try {
|
|
$files = $finder->name('*Job.php')->in($domain->realPath.DIRECTORY_SEPARATOR.'Jobs')->files();
|
|
} catch (\Exception $e) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($files as $file) {
|
|
$name = $file->getRelativePathName();
|
|
$job = new Job(
|
|
$this->realName($name, '/Job.php/'),
|
|
$this->getNamespace($name),
|
|
$name,
|
|
$file->getRealPath(),
|
|
$this->relativeFromReal($file->getRealPath()),
|
|
$domain,
|
|
file_get_contents($file->getRealPath())
|
|
);
|
|
|
|
$jobs[$domain->name]->push($job);
|
|
}
|
|
}
|
|
|
|
return $jobs;
|
|
}
|
|
|
|
/**
|
|
* Get the list of services,
|
|
* optionally withing a specified domain.
|
|
*
|
|
* @param string $domainName
|
|
*
|
|
* @throws \Exception
|
|
* @return \Illuminate\Support\Collection
|
|
*/
|
|
public function listServices($domainName = '')
|
|
{
|
|
$domains = $this->listDomains();
|
|
|
|
if (!empty($domainName)) {
|
|
$domains = $domains->filter(function ($domain) use ($domainName) {
|
|
return $domain->name === $domainName || $domain->slug === $domainName;
|
|
});
|
|
|
|
if ($domains->isEmpty()) {
|
|
throw new InvalidArgumentException('Domain "' . $domainName . '" could not be found.');
|
|
}
|
|
}
|
|
|
|
$services = [];
|
|
|
|
foreach ($domains as $domain) {
|
|
$services[$domain->name] = new Collection();
|
|
$finder = new SymfonyFinder();
|
|
|
|
try {
|
|
$files = $finder->name('*Service.php')->in($domain->realPath.DIRECTORY_SEPARATOR.'Services')->files();
|
|
} catch (\Exception $e) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($files as $file) {
|
|
$fileName = $file->getRelativePathName();
|
|
$title = $this->realName($fileName, '/Service.php/');
|
|
$realPath = $file->getRealPath();
|
|
$relativePath = $this->relativeFromReal($realPath);
|
|
|
|
$services[$domain->name]->push(new Service($title, $fileName, $realPath, $relativePath, $domain));
|
|
}
|
|
}
|
|
|
|
return $services;
|
|
}
|
|
|
|
/**
|
|
* 获取Domain的 ServiceProvider
|
|
*
|
|
* @return void
|
|
*/
|
|
public function getDomainServiceProvider($domain)
|
|
{
|
|
if ($domain instanceof Domain) {
|
|
return $domain->namespace . '\\Providers\\' . $domain->name . 'ServiceProvider';
|
|
}
|
|
|
|
$rootNamespace = $this->findRootNamespace();
|
|
$domainNamespace = $this->findDomainNamespace($name);
|
|
return $domainNamespace . '\\Providers\\' . $name . 'ServiceProvider';
|
|
}
|
|
|
|
/**
|
|
* Get the path to the cached packages.php file.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCachedPackagesPath()
|
|
{
|
|
return storage_path('framework/cache/packages.php');
|
|
}
|
|
|
|
/**
|
|
* Get the relative version of the given real path.
|
|
*
|
|
* @param string $path
|
|
* @param string $needle
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function relativeFromReal($path, $needle = '')
|
|
{
|
|
if (!$needle) {
|
|
$needle = $this->getSourceDirectoryName() . '/';
|
|
}
|
|
|
|
return strstr($path, $needle);
|
|
}
|
|
|
|
/**
|
|
* Determine the real name of the given name,
|
|
* excluding the given pattern.
|
|
* i.e. the name: "CreateArticleService.php" with pattern '/Service.php'
|
|
* will result in "Create Article".
|
|
*
|
|
* @param string $name
|
|
* @param string $pattern
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function realName($name, $pattern = '//')
|
|
{
|
|
$name = preg_replace($pattern, '', $name);
|
|
|
|
return implode(' ', preg_split('/(?=[A-Z])/', $name, -1, PREG_SPLIT_NO_EMPTY));
|
|
}
|
|
}
|