初始化代码

This commit is contained in:
2025-12-22 14:34:25 +08:00
parent c2c5ae2fdd
commit a77dbc743f
1510 changed files with 213008 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
<?php
namespace Swoole\IDEHelper;
use ReflectionClass;
use ReflectionException;
use ReflectionExtension;
use ReflectionParameter;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use Swoole\IDEHelper\Rules\NamespaceRule;
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Reflection\ClassReflection;
/**
* Class AbstractStubGenerator
*
* @package Swoole\IDEHelper
*/
abstract class AbstractStubGenerator
{
const C_METHOD = 1;
const C_PROPERTY = 2;
const C_CONSTANT = 3;
/**
* @var string
*/
protected $extension;
/**
* @var string
*/
protected $language;
/**
* @var string
*/
protected $dirConfig;
/**
* @var string
*/
protected $dirOutput;
/**
* @var ReflectionExtension
*/
protected $rf_ext;
const ALIAS_SHORT_NAME = 1; // Short names of coroutine classes.
const ALIAS_SNAKE_CASE = 2; // Class names in snake_case. e.g., swoole_timer.
protected $aliases = [
self::ALIAS_SHORT_NAME => [],
self::ALIAS_SNAKE_CASE => [],
];
/**
* Methods that don't need to have return type specified.
*/
const IGNORED_METHODS = [
'__construct' => null,
'__destruct' => null,
];
/**
* AbstractStubGenerator constructor.
*
* @throws Exception
* @throws ReflectionException
*/
public function __construct()
{
$this->init();
if (!extension_loaded($this->extension)) {
throw new Exception("Extension $this->extension not enabled or not installed.");
}
$this->language = 'chinese';
$this->dirOutput = dirname(__DIR__) . '/output/' . $this->extension;
$this->dirConfig = dirname(__DIR__) . '/config';
$this->rf_ext = new ReflectionExtension($this->extension);
}
/**
* @throws Exception
* @throws ReflectionException
*/
public function export(): void
{
// Retrieve and save all constants.
if ($this->rf_ext->getConstants()) {
$defines = '';
foreach ($this->rf_ext->getConstants() as $name => $value) {
$defines .= sprintf("define('%s', %s);\n", $name, (is_numeric($value) ? $value : "'{$value}'"));
}
$this->writeToPhpFile($this->dirOutput . '/constants.php', $defines);
}
// Retrieve and save all functions.
$output = $this->getFunctionsDef();
if (!empty($output)) {
$this->writeToPhpFile($this->dirOutput . '/functions.php', $output);
}
// Retrieve and save all classes.
$classes = $this->rf_ext->getClasses();
// There are three types of class names in Swoole:
// 1. short name of a class. Short names start with "Co\", and they can be found in file output/aliases.php.
// 2. fully qualified name (class name with namespace prefix), e.g., \Swoole\Timer. These classes can be found
// under folder output/namespace.
// 3. snake_case. e.g., swoole_timer. These aliases can be found in file output/aliases.php.
foreach ($classes as $className => $ref) {
if (strtolower(substr($className, 0, 3)) == 'co\\') {
$className = str_replace('Swoole\\Coroutine', 'Co', $ref->getName());
$this->aliases[self::ALIAS_SHORT_NAME][$className] = $ref->getName();
} elseif (strchr($className, '\\')) {
$this->exportNamespaceClass($className, $ref);
} else {
$this->aliases[self::ALIAS_SNAKE_CASE][$className] = $this->getNamespaceAlias($className);
}
}
$class_alias = '';
foreach (array_filter($this->aliases) as $type => $aliases) {
if (!empty($class_alias)) {
$class_alias .= "\n";
}
asort($aliases);
foreach ($aliases as $alias => $original) {
$class_alias .= "class_alias({$original}::class, {$alias}::class);\n";
}
}
$this->writeToPhpFile($this->dirOutput . '/aliases.php', $class_alias);
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->rf_ext->getVersion();
}
/**
* @return string
*/
public function getExtension(): string
{
return $this->extension;
}
/**
* @param string $extension
* @return $this
*/
public function setExtension(string $extension): self
{
$this->extension = $extension;
return $this;
}
/**
* @param string $className
* @return string
*/
protected function getNamespaceAlias(string $className): string
{
if (strcasecmp($className, 'co') === 0) {
return Coroutine::class;
} elseif (strcasecmp($className, 'chan') === 0) {
return Channel::class;
} else {
return str_replace('_', '\\', ucwords($className, '_'));
}
}
/**
* @param string $class
* @param string $name
* @param string $type
* @return array
*/
protected function getConfig(string $class, string $name, string $type): array
{
switch ($type) {
case self::C_CONSTANT:
$dir = 'constant';
break;
case self::C_METHOD:
$dir = 'method';
break;
case self::C_PROPERTY:
$dir = 'property';
break;
default:
return false;
}
$file = $this->dirConfig . '/' . $this->language . '/' . strtolower($class) . '/' . $dir . '/' . $name . '.php';
if (is_file($file)) {
return include $file;
} else {
return array();
}
}
/**
* @param ReflectionParameter $parameter
* @return string|null
*/
protected function getDefaultValue(ReflectionParameter $parameter): ?string
{
try {
$default_value = $parameter->getDefaultValue();
if ($default_value === []) {
$default_value = '[]';
} elseif ($default_value === null) {
$default_value = 'null';
} elseif (is_bool($default_value)) {
$default_value = $default_value ? 'true' : 'false';
} else {
$default_value = var_export($default_value, true);
}
} catch (\Throwable $e) {
if ($parameter->isOptional()) {
$default_value = 'null';
} else {
$default_value = null;
}
}
return $default_value;
}
/**
* @return string
*/
protected function getFunctionsDef(): string
{
$all = '';
foreach ($this->rf_ext->getFunctions() as $function) {
$vp = array();
$comment = "/**\n";
$params = $function->getParameters();
foreach ($params as $param) {
$default_value = $this->getDefaultValue($param);
$comment .= " * @param \${$param->name}[" .
($param->isOptional() ? 'optional' : 'required') .
"]\n";
$vp[] = ($param->isPassedByReference() ? '&' : '') .
"\${$param->name}" .
($default_value ? " = {$default_value}" : '');
}
$comment .= " * @return mixed\n";
$comment .= " */\n";
$comment .= sprintf("function %s(%s){}\n\n", $function->getName(), join(', ', $vp));
$all .= $comment;
}
return $all;
}
/**
* @param string $classname
* @param ReflectionClass $ref
* @throws Exception
* @throws ReflectionException
*/
protected function exportNamespaceClass(string $classname, ReflectionClass $ref): void
{
(new NamespaceRule($this))->validate($classname);
$class = ClassGenerator::fromReflection(new ClassReflection($ref->getName()));
foreach ($class->getMethods() as $method) {
if ((null === $method->getReturnType()) && !array_key_exists($method->getName(), self::IGNORED_METHODS)) {
$method->setDocBlock(
DocBlockGenerator::fromArray(
[
'shortDescription' => null,
'longDescription' => null,
'tags' => [
new ReturnTag(
[
'datatype' => 'mixed',
]
),
],
]
)
);
}
}
$this->writeToPhpFile(
$this->dirOutput . '/namespace/' . implode('/', array_slice(explode('\\', $classname), 1)) . '.php',
$class->generate()
);
}
/**
* @param string $path
* @param string $content
* @return AbstractStubGenerator
*/
protected function writeToPhpFile(string $path, string $content): self
{
$this->mkdir(dirname($path));
file_put_contents($path, "<?php\n\n" . $content);
return $this;
}
/**
* @param string $dir
* @return AbstractStubGenerator
*/
protected function mkdir(string $dir): self
{
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
return $this;
}
/**
* @return AbstractStubGenerator
*/
abstract protected function init(): AbstractStubGenerator;
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Swoole\IDEHelper;
/**
* Class Constant
*
* @package Swoole\IDEHelper
*/
class Constant
{
const EXT_SWOOLE = 'swoole';
const EXT_SWOOLE_ASYNC = 'swoole_async';
const EXT_SWOOLE_ORM = 'swoole_orm';
const EXT_SWOOLE_POSTGRESQL = 'swoole_postgresql';
const EXT_SWOOLE_SERIALIZE = 'swoole_serialize';
const EXT_SWOOLE_ZOOKEEPER = 'swoole_zookeeper';
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Swoole\IDEHelper;
class Exception extends \Exception
{
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Swoole\IDEHelper\Rules;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Exception;
/**
* Class NamespaceRule
*
* @package Swoole\IDEHelper\Rules
*/
abstract class AbstractRule
{
/**
* @var AbstractStubGenerator
*/
protected $generator;
/**
* AbstractRule constructor.
*
* @param AbstractStubGenerator $generator
*/
public function __construct(AbstractStubGenerator $generator)
{
$this->setGenerator($generator);
}
/**
* @param mixed ...$params
*/
public function validate(...$params): void
{
if (in_array($this->getGenerator()->getExtension(), $this->getEnabledExtensions())) {
$this->validateWith(...$params);
}
}
/**
* @return AbstractStubGenerator
*/
protected function getGenerator(): AbstractStubGenerator
{
return $this->generator;
}
/**
* @param AbstractStubGenerator $generator
* @return $this
*/
protected function setGenerator(AbstractStubGenerator $generator): self
{
$this->generator = $generator;
return $this;
}
/**
* @param mixed ...$params
* @throws Exception
*/
abstract protected function validateWith(...$params): void;
/**
* @return array
*/
abstract protected function getEnabledExtensions(): array;
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Swoole\IDEHelper\Rules;
use Swoole\IDEHelper\Constant;
use Swoole\IDEHelper\Exception;
/**
* Class NamespaceRule
*
* @package Swoole\IDEHelper\Rules
*/
class NamespaceRule extends AbstractRule
{
/**
* @inheritDoc
*/
protected function validateWith(...$params): void
{
if (strcasecmp(explode('\\', $params[0])[0], $this->getGenerator()->getExtension()) !== 0) {
throw new Exception(
"Class $params[0] should be under namespace \\{$this->getGenerator()->getExtension()} but not."
);
}
}
/**
* @inheritDoc
*/
protected function getEnabledExtensions(): array
{
return [
Constant::EXT_SWOOLE,
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class Swoole
*
* @package Swoole\IDEHelper\StubGenerators
*/
class Swoole extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class SwooleAsync
*
* @package Swoole\IDEHelper\StubGenerators
*/
class SwooleAsync extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE_ASYNC;
return $this;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
use Swoole\IDEHelper\Exception;
use Symfony\Component\Filesystem\Filesystem;
/**
* Class SwooleLib
*
* @package Swoole\IDEHelper\StubGenerators
* @see https://github.com/swoole/swoole-src/tree/master/library
*/
class SwooleLib extends AbstractStubGenerator
{
const EXTRA_FILES = [
"ext",
"std",
"README.md",
"config.inc",
];
/**
* @var string
*/
protected $libDir;
/**
* AbstractStubGenerator constructor.
*
* @throws Exception
* @throws ReflectionException
*/
public function __construct()
{
parent::__construct();
$this->extension = "swoole_lib";
$this->dirOutput = dirname($this->dirOutput) . DIRECTORY_SEPARATOR . $this->extension;
}
/**
* @inheritDoc
*/
public function export(): void
{
$this->mkdir($this->dirOutput);
$fileSystem = new Filesystem();
$fileSystem->mirror($this->libDir, $this->dirOutput);
foreach (self::EXTRA_FILES as $file) {
$fileSystem->remove($this->dirOutput . DIRECTORY_SEPARATOR . $file);
}
}
/**
* @inheritDoc
* @throws Exception
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE;
if (!empty($_SERVER["SWOOLE_LIB_DIR"])) {
$this->libDir = $_SERVER["SWOOLE_LIB_DIR"];
} elseif (!empty($_SERVER["SWOOLE_SRC_DIR"])) {
$this->libDir = $_SERVER["SWOOLE_SRC_DIR"] . DIRECTORY_SEPARATOR . "library";
}
if (empty($this->libDir)) {
throw new Exception("Swoole library directory is not specified.");
} elseif (!is_dir($this->libDir)) {
throw new Exception("Swoole library directory \"{$this->libDir}\" is invalid.");
}
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class SwooleOrm
*
* @package Swoole\IDEHelper\StubGenerators
*/
class SwooleOrm extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE_ORM;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class SwoolePostgresql
*
* @package Swoole\IDEHelper\StubGenerators
*/
class SwoolePostgresql extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE_POSTGRESQL;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class SwooleSerialize
*
* @package Swoole\IDEHelper\StubGenerators
*/
class SwooleSerialize extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE_SERIALIZE;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Swoole\IDEHelper\StubGenerators;
use Swoole\IDEHelper\AbstractStubGenerator;
use Swoole\IDEHelper\Constant;
/**
* Class SwooleZookeeper
*
* @package Swoole\IDEHelper\StubGenerators
*/
class SwooleZookeeper extends AbstractStubGenerator
{
/**
* @inheritDoc
*/
protected function init(): AbstractStubGenerator
{
$this->extension = Constant::EXT_SWOOLE_ZOOKEEPER;
return $this;
}
}