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 @@
+