diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fb6d90b --- /dev/null +++ b/LICENSE @@ -0,0 +1,166 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..05639a5 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0-dev diff --git a/bin/Console.php b/bin/Console.php new file mode 100644 index 0000000..5430e5d --- /dev/null +++ b/bin/Console.php @@ -0,0 +1,434 @@ + array('n', 'no', 'false', 0, '0', true), + 1 => array('y', 'yes', 'true', 1, '1', false, null) + ); + + /** + * CliInput constructor. + * + * @param array $argv + */ + public function __construct($argv = null) + { + $this->parseArgv($argv ?: $_SERVER['argv']); + + $this->init(); + } + + /** + * init + * + * @return void + */ + protected function init() + { + // Override if necessary + } + + /** + * execute + * + * @param \Closure|null $callback + * + * @return int + */ + public function execute(\Closure $callback = null) + { + try { + if ($this->getOption($this->helpOptions)) { + $this->out($this->getHelp()); + + return 0; + } + + if ($callback) { + if (PHP_VERSION_ID >= 50400) { + $callback = $callback->bindTo($this); + } + + $result = call_user_func($callback, $this); + } else { + $result = $this->doExecute(); + } + } catch (\Exception $e) { + $result = $this->handleException($e); + } catch (\Throwable $e) { + $result = $this->handleException($e); + } + + if ($result === true) { + $result = 0; + } elseif ($result === false) { + $result = 255; + } else { + $result = (bool) $result; + } + + return (int) $result; + } + + /** + * doExecute + * + * @return mixed + */ + protected function doExecute() + { + // Please override this method. + return 0; + } + + /** + * delegate + * + * @param string $method + * + * @return mixed + */ + protected function delegate($method) + { + $args = func_get_args(); + array_shift($args); + + if (!is_callable(array($this, $method))) { + throw new \LogicException(sprintf('Method: %s not found', $method)); + } + + return call_user_func_array(array($this, $method), $args); + } + + /** + * getHelp + * + * @return string + */ + protected function getHelp() + { + return trim($this->help); + } + + /** + * handleException + * + * @param \Exception|\Throwable $e + * + * @return int + */ + protected function handleException($e) + { + $v = $this->getOption('v'); + + if ($e instanceof CommandArgsException) { + $this->err('[Warning] ' . $e->getMessage()) + ->err() + ->err($this->getHelp()); + } else { + $this->err('[Error] ' . $e->getMessage()); + } + + if ($v) { + $this->err('[Backtrace]:') + ->err($e->getTraceAsString()); + } + + $code = $e->getCode(); + + return $code === 0 ? 255 : $code; + } + + /** + * getArgument + * + * @param int $offset + * @param mixed $default + * + * @return mixed|null + */ + public function getArgument($offset, $default = null) + { + if (!isset($this->args[$offset])) { + return $default; + } + + return $this->args[$offset]; + } + + /** + * setArgument + * + * @param int $offset + * @param mixed $value + * + * @return static + */ + public function setArgument($offset, $value) + { + $this->args[$offset] = $value; + + return $this; + } + + /** + * getOption + * + * @param string|array $name + * @param mixed $default + * + * @return mixed|null + */ + public function getOption($name, $default = null) + { + $name = (array) $name; + + foreach ($name as $n) { + if (isset($this->options[$n])) { + return $this->options[$n]; + } + } + + return $default; + } + + /** + * setOption + * + * @param string|array $name + * @param mixed $value + * + * @return static + */ + public function setOption($name, $value) + { + $name = (array) $name; + + foreach ($name as $n) { + $this->options[$n] = $value; + } + + return $this; + } + + /** + * out + * + * @param string $text + * @param boolean $nl + * + * @return static + */ + public function out($text = null, $nl = true) + { + fwrite(STDOUT, $text . ($nl ? "\n" : '')); + + return $this; + } + + /** + * err + * + * @param string $text + * @param boolean $nl + * + * @return static + */ + public function err($text = null, $nl = true) + { + fwrite(STDERR, $text . ($nl ? "\n" : '')); + + return $this; + } + + /** + * in + * + * @param string $ask + * @param mixed $default + * + * @return string + */ + public function in($ask = '', $default = null, $bool = false) + { + $this->out($ask, false); + + $in = rtrim(fread(STDIN, 8192), "\n\r"); + + if ($bool) { + $in = $in === '' ? $default : $in; + + return (bool) $this->mapBoolean($in); + } + + return $in === '' ? (string) $default : $in; + } + + /** + * mapBoolean + * + * @param string $in + * + * @return bool + */ + public function mapBoolean($in) + { + $in = strtolower((string) $in); + + if (in_array($in, $this->booleanMapping[0], true)) { + return false; + } + + if (in_array($in, $this->booleanMapping[1], true)) { + return true; + } + + return null; + } + + /** + * exec + * + * @param string $command + * + * @return static + */ + protected function exec($command) + { + $this->out('>> ' . $command); + + system($command); + + return $this; + } + + /** + * parseArgv + * + * @param array $argv + * + * @return void + */ + protected function parseArgv($argv) + { + $this->executable = array_shift($argv); + $key = null; + + $out = array(); + + for ($i = 0, $j = count($argv); $i < $j; $i++) { + $arg = $argv[$i]; + + // --foo --bar=baz + if (0 === strpos($arg, '--')) { + $eqPos = strpos($arg, '='); + + // --foo + if ($eqPos === false) { + $key = substr($arg, 2); + + // --foo value + if ($i + 1 < $j && $argv[$i + 1][0] !== '-') { + $value = $argv[$i + 1]; + $i++; + } else { + $value = isset($out[$key]) ? $out[$key] : true; + } + + $out[$key] = $value; + } else { + // --bar=baz + $key = substr($arg, 2, $eqPos - 2); + $value = substr($arg, $eqPos + 1); + $out[$key] = $value; + } + } elseif (0 === strpos($arg, '-')) { + // -k=value -abc + + // -k=value + if (isset($arg[2]) && $arg[2] === '=') { + $key = $arg[1]; + $value = substr($arg, 3); + $out[$key] = $value; + } else { + // -abc + $chars = str_split(substr($arg, 1)); + + foreach ($chars as $char) { + $key = $char; + $out[$key] = isset($out[$key]) ? $out[$key] + 1 : 1; + } + + // -a a-value + if (($i + 1 < $j) && ($argv[$i + 1][0] !== '-') && (count($chars) === 1)) { + $out[$key] = $argv[$i + 1]; + $i++; + } + } + } else { + // Plain-arg + $this->args[] = $arg; + } + } + + $this->options = $out; + } +} + +class CommandArgsException extends \RuntimeException +{ +} diff --git a/bin/release.php b/bin/release.php new file mode 100644 index 0000000..8ff2c4b --- /dev/null +++ b/bin/release.php @@ -0,0 +1,182 @@ + + +[Options] + h | help Show help information + v Show more debug information. + --dry-run Dry run without git push or commit. +HELP; + + /** + * doExecute + * + * @return bool|mixed + * + * @since __DEPLOY_VERSION__ + */ + protected function doExecute() + { + $currentVersion = trim(file_get_contents(__DIR__ . '/../VERSION')); + $targetVersion = $this->getArgument(0); + + if (!$targetVersion) { + $targetVersion = static::versionPlus($currentVersion, 1); + } + + $this->out('Release version: ' . $targetVersion); + + static::writeVersion($targetVersion); + $this->replaceDocblockTags($targetVersion); + + $this->exec(sprintf('git commit -am "Release version: %s"', $targetVersion)); + $this->exec(sprintf('git tag %s', $targetVersion)); + + $this->exec('git push'); + $this->exec('git push --tags'); + + return true; + } + + /** + * writeVersion + * + * @param string $version + * + * @return bool|int + * + * @since __DEPLOY_VERSION__ + */ + protected static function writeVersion(string $version) + { + return file_put_contents(static::versionFile(), $version . "\n"); + } + + /** + * versionFile + * + * @return string + * + * @since __DEPLOY_VERSION__ + */ + protected static function versionFile(): string + { + return __DIR__ . '/../VERSION'; + } + + /** + * versionPlus + * + * @param string $version + * @param int $offset + * @param string $suffix + * + * @return string + * + * @since __DEPLOY_VERSION__ + */ + protected static function versionPlus(string $version, int $offset, string $suffix = ''): string + { + [$version] = explode('-', $version, 2); + + $numbers = explode('.', $version); + + if (!isset($numbers[2])) { + $numbers[2] = 0; + } + + $numbers[2] += $offset; + + if ($numbers[2] === 0) { + unset($numbers[2]); + } + + $version = implode('.', $numbers); + + if ($suffix) { + $version .= '-' . $suffix; + } + + return $version; + } + + /** + * replaceDocblockTags + * + * @param string $version + * + * @return void + */ + protected function replaceDocblockTags(string $version): void + { + $this->out('Replacing Docblock...'); + + $files = new RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( + __DIR__ . '/../src', + \FilesystemIterator::SKIP_DOTS + ) + ); + + /** @var \SplFileInfo $file */ + foreach ($files as $file) { + if ($file->isDir() || $file->getExtension() !== 'php') { + continue; + } + + $content = file_get_contents($file->getPathname()); + + $content = str_replace( + ['{DEPLOY_VERSION}', '__DEPLOY_VERSION__', '__LICENSE__', '${ORGANIZATION}', '{ORGANIZATION}'], + [$version, $version, 'MIT', 'LYRASOFT', 'LYRASOFT'], + $content + ); + + file_put_contents($file->getPathname(), $content); + } + + $this->exec('git checkout master'); + $this->exec(sprintf('git commit -am "Prepare for %s release."', $version)); + $this->exec('git push origin master'); + } + + /** + * exec + * + * @param string $command + * + * @return static + */ + protected function exec($command) + { + $this->out('>> ' . $command); + + if (!$this->getOption('dry-run')) { + system($command); + } + + return $this; + } +} + +exit((new Build())->execute()); diff --git a/composer.json b/composer.json index 1c19425..62ded5f 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,10 @@ "psr-4": { "Lyrasoft\\Toolkit\\": "src/", "Windwalker\\": "src/Windwalker/" - } + }, + "files": [ + "src/bootstrap.php" + ] }, "authors": [ { diff --git a/src/bootstrap.php b/src/bootstrap.php new file mode 100644 index 0000000..2c19f87 --- /dev/null +++ b/src/bootstrap.php @@ -0,0 +1,10 @@ +