diff --git a/addons/weliam_smartcity/vendor/autoload.php b/addons/weliam_smartcity/vendor/autoload.php new file mode 100644 index 0000000..403194b --- /dev/null +++ b/addons/weliam_smartcity/vendor/autoload.php @@ -0,0 +1,7 @@ +> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi + +script: + - cd ext/pimple + - if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi + - if [ "$PIMPLE_EXT" == "yes" ]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi + - cd ../.. + - ./vendor/bin/simple-phpunit + +matrix: + include: + - php: hhvm + dist: trusty + env: PIMPLE_EXT=no + exclude: + - php: 7.0 + env: PIMPLE_EXT=yes + - php: 7.1 + env: PIMPLE_EXT=yes diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/CHANGELOG b/addons/weliam_smartcity/vendor/pimple/pimple/CHANGELOG new file mode 100644 index 0000000..ba56760 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/CHANGELOG @@ -0,0 +1,59 @@ +* 3.2.3 (2017-XX-XX) + + * n/a + +* 3.2.2 (2017-07-23) + + * reverted extending a protected closure throws an exception (deprecated it instead) + +* 3.2.1 (2017-07-17) + + * fixed PHP error + +* 3.2.0 (2017-07-17) + + * added a PSR-11 service locator + * added a PSR-11 wrapper + * added ServiceIterator + * fixed extending a protected closure (now throws InvalidServiceIdentifierException) + +* 3.1.0 (2017-07-03) + + * deprecated the C extension + * added support for PSR-11 exceptions + +* 3.0.2 (2015-09-11) + + * refactored the C extension + * minor non-significant changes + +* 3.0.1 (2015-07-30) + + * simplified some code + * fixed a segfault in the C extension + +* 3.0.0 (2014-07-24) + + * removed the Pimple class alias (use Pimple\Container instead) + +* 2.1.1 (2014-07-24) + + * fixed compiler warnings for the C extension + * fixed code when dealing with circular references + +* 2.1.0 (2014-06-24) + + * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a + deprecated alias which will be removed in Pimple 3.0) + * added Pimple\ServiceProviderInterface (and Pimple::register()) + +* 2.0.0 (2014-02-10) + + * changed extend to automatically re-assign the extended service and keep it as shared or factory + (to keep BC, extend still returns the extended service) + * changed services to be shared by default (use factory() for factory + services) + +* 1.0.0 + + * initial version diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/LICENSE b/addons/weliam_smartcity/vendor/pimple/pimple/LICENSE new file mode 100644 index 0000000..e02dc5a --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2017 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/README.rst b/addons/weliam_smartcity/vendor/pimple/pimple/README.rst new file mode 100644 index 0000000..a03b6d3 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/README.rst @@ -0,0 +1,326 @@ +Pimple +====== + +.. caution:: + + This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read + the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good + way to learn more about how to create a simple Dependency Injection + Container (recent versions of Pimple are more focused on performance). + +Pimple is a small Dependency Injection Container for PHP. + +Installation +------------ + +Before using Pimple in your project, add it to your ``composer.json`` file: + +.. code-block:: bash + + $ ./composer.phar require pimple/pimple "^3.0" + +Usage +----- + +Creating a container is a matter of creating a ``Container`` instance: + +.. code-block:: php + + use Pimple\Container; + + $container = new Container(); + +As many other dependency injection containers, Pimple manages two different +kind of data: **services** and **parameters**. + +Defining Services +~~~~~~~~~~~~~~~~~ + +A service is an object that does something as part of a larger system. Examples +of services: a database connection, a templating engine, or a mailer. Almost +any **global** object can be a service. + +Services are defined by **anonymous functions** that return an instance of an +object: + +.. code-block:: php + + // define some services + $container['session_storage'] = function ($c) { + return new SessionStorage('SESSION_ID'); + }; + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + +Notice that the anonymous function has access to the current container +instance, allowing references to other services or parameters. + +As objects are only created when you get them, the order of the definitions +does not matter. + +Using the defined services is also very easy: + +.. code-block:: php + + // get the session object + $session = $container['session']; + + // the above call is roughly equivalent to the following code: + // $storage = new SessionStorage('SESSION_ID'); + // $session = new Session($storage); + +Defining Factory Services +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, each time you get a service, Pimple returns the **same instance** +of it. If you want a different instance to be returned for all calls, wrap your +anonymous function with the ``factory()`` method + +.. code-block:: php + + $container['session'] = $container->factory(function ($c) { + return new Session($c['session_storage']); + }); + +Now, each call to ``$container['session']`` returns a new instance of the +session. + +Defining Parameters +~~~~~~~~~~~~~~~~~~~ + +Defining a parameter allows to ease the configuration of your container from +the outside and to store global values: + +.. code-block:: php + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +If you change the ``session_storage`` service definition like below: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + +You can now easily change the cookie name by overriding the +``cookie_name`` parameter instead of redefining the service +definition. + +Protecting Parameters +~~~~~~~~~~~~~~~~~~~~~ + +Because Pimple sees anonymous functions as service definitions, you need to +wrap anonymous functions with the ``protect()`` method to store them as +parameters: + +.. code-block:: php + + $container['random_func'] = $container->protect(function () { + return rand(); + }); + +Modifying Services after Definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases you may want to modify a service definition after it has been +defined. You can use the ``extend()`` method to define additional code to be +run on your service just after it is created: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + + $container->extend('session_storage', function ($storage, $c) { + $storage->...(); + + return $storage; + }); + +The first argument is the name of the service to extend, the second a function +that gets access to the object instance and the container. + +Extending a Container +~~~~~~~~~~~~~~~~~~~~~ + +If you use the same libraries over and over, you might want to reuse some +services from one project to the next one; package your services into a +**provider** by implementing ``Pimple\ServiceProviderInterface``: + +.. code-block:: php + + use Pimple\Container; + + class FooProvider implements Pimple\ServiceProviderInterface + { + public function register(Container $pimple) + { + // register some services and parameters + // on $pimple + } + } + +Then, register the provider on a Container: + +.. code-block:: php + + $pimple->register(new FooProvider()); + +Fetching the Service Creation Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method: + +.. code-block:: php + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + + $sessionFunction = $container->raw('session'); + +PSR-11 compatibility +-------------------- + +For historical reasons, the ``Container`` class does not implement the PSR-11 +``ContainerInterface``. However, Pimple provides a helper class that will let +you decouple your code from the Pimple container class. + +The PSR-11 container class +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Pimple\Psr11\Container`` class lets you access the content of an +underlying Pimple container using ``Psr\Container\ContainerInterface`` +methods: + +.. code-block:: php + + use Pimple\Container; + use Pimple\Psr11\Container as PsrContainer; + + $container = new Container(); + $container['service'] = function ($c) { + return new Service(); + }; + $psr11 = new PsrContainer($container); + + $controller = function (PsrContainer $container) { + $service = $container->get('service'); + }; + $controller($psr11); + +Using the PSR-11 ServiceLocator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes, a service needs access to several other services without being sure +that all of them will actually be used. In those cases, you may want the +instantiation of the services to be lazy. + +The traditional solution is to inject the entire service container to get only +the services really needed. However, this is not recommended because it gives +services a too broad access to the rest of the application and it hides their +actual dependencies. + +The ``ServiceLocator`` is intended to solve this problem by giving access to a +set of predefined services while instantiating them only when actually needed. + +It also allows you to make your services available under a different name than +the one used to register them. For instance, you may want to use an object +that expects an instance of ``EventDispatcherInterface`` to be available under +the name ``event_dispatcher`` while your event dispatcher has been +registered under the name ``dispatcher``: + +.. code-block:: php + + use Monolog\Logger; + use Pimple\Psr11\ServiceLocator; + use Psr\Container\ContainerInterface; + use Symfony\Component\EventDispatcher\EventDispatcher; + + class MyService + { + /** + * "logger" must be an instance of Psr\Log\LoggerInterface + * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + private $services; + + public function __construct(ContainerInterface $services) + { + $this->services = $services; + } + } + + $container['logger'] = function ($c) { + return new Monolog\Logger(); + }; + $container['dispatcher'] = function () { + return new EventDispatcher(); + }; + + $container['service'] = function ($c) { + $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher')); + + return new MyService($locator); + }; + +Referencing a Collection of Services Lazily +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing a collection of services instances in an array may prove inefficient +if the class that consumes the collection only needs to iterate over it at a +later stage, when one of its method is called. It can also lead to problems +if there is a circular dependency between one of the services stored in the +collection and the class that consumes it. + +The ``ServiceIterator`` class helps you solve these issues. It receives a +list of service names during instantiation and will retrieve the services +when iterated over: + +.. code-block:: php + + use Pimple\Container; + use Pimple\ServiceIterator; + + class AuthorizationService + { + private $voters; + + public function __construct($voters) + { + $this->voters = $voters; + } + + public function canAccess($resource) + { + foreach ($this->voters as $voter) { + if (true === $voter->canAccess($resource) { + return true; + } + } + + return false; + } + } + + $container = new Container(); + + $container['voter1'] = function ($c) { + return new SomeVoter(); + } + $container['voter2'] = function ($c) { + return new SomeOtherVoter($c['auth']); + } + $container['auth'] = function ($c) { + return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2')); + } + +.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1 diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/composer.json b/addons/weliam_smartcity/vendor/pimple/pimple/composer.json new file mode 100644 index 0000000..dabf190 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/composer.json @@ -0,0 +1,29 @@ +{ + "name": "pimple/pimple", + "type": "library", + "description": "Pimple, a simple Dependency Injection Container", + "keywords": ["dependency injection", "container"], + "homepage": "http://pimple.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" + }, + "autoload": { + "psr-0": { "Pimple": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/.gitignore b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/.gitignore new file mode 100644 index 0000000..1861088 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/.gitignore @@ -0,0 +1,30 @@ +*.sw* +.deps +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +build/ +config.cache +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +*.loT +.libs/ +modules/ +*.la +*.lo diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/README.md b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/README.md new file mode 100644 index 0000000..7b39eb2 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/README.md @@ -0,0 +1,12 @@ +This is Pimple 2 implemented in C + +* PHP >= 5.3 +* Not tested under Windows, might work + +Install +======= + + > phpize + > ./configure + > make + > make install diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.m4 b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.m4 new file mode 100644 index 0000000..3a6e9aa --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension pimple + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(pimple, for pimple support, +dnl Make sure that the comment is aligned: +dnl [ --with-pimple Include pimple support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(pimple, whether to enable pimple support, +dnl Make sure that the comment is aligned: +[ --enable-pimple Enable pimple support]) + +if test "$PHP_PIMPLE" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-pimple -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/pimple.h" # you most likely want to change this + dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter + dnl PIMPLE_DIR=$PHP_PIMPLE + dnl else # search default path list + dnl AC_MSG_CHECKING([for pimple files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl PIMPLE_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$PIMPLE_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the pimple distribution]) + dnl fi + + dnl # --with-pimple -> add include path + dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include) + + dnl # --with-pimple -> check for lib and symbol presence + dnl LIBNAME=pimple # you may want to change this + dnl LIBSYMBOL=pimple # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_PIMPLELIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong pimple lib version or lib not found]) + dnl ],[ + dnl -L$PIMPLE_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(PIMPLE_SHARED_LIBADD) + + PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared) +fi diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.w32 b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.w32 new file mode 100644 index 0000000..39857b3 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("pimple", "for pimple support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("pimple", "enable pimple support", "no"); + +if (PHP_PIMPLE != "no") { + EXTENSION("pimple", "pimple.c"); +} + diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/php_pimple.h b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/php_pimple.h new file mode 100644 index 0000000..eed7c17 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/php_pimple.h @@ -0,0 +1,137 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PHP_PIMPLE_H +#define PHP_PIMPLE_H + +extern zend_module_entry pimple_module_entry; +#define phpext_pimple_ptr &pimple_module_entry + +#ifdef PHP_WIN32 +# define PHP_PIMPLE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_PIMPLE_API __attribute__ ((visibility("default"))) +#else +# define PHP_PIMPLE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#define PIMPLE_VERSION "3.2.3-DEV" + +#define PIMPLE_NS "Pimple" +#define PSR_CONTAINER_NS "Psr\\Container" +#define PIMPLE_EXCEPTION_NS "Pimple\\Exception" + +#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5 +#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10 + +#define PIMPLE_DEPRECATE do { \ + int er = EG(error_reporting); \ + EG(error_reporting) = 0;\ + php_error(E_DEPRECATED, "The Pimple C extension is deprecated since version 3.1 and will be removed in 4.0."); \ + EG(error_reporting) = er; \ +} while (0); + +zend_module_entry *get_module(void); + +PHP_MINIT_FUNCTION(pimple); +PHP_MINFO_FUNCTION(pimple); + +PHP_METHOD(FrozenServiceException, __construct); +PHP_METHOD(InvalidServiceIdentifierException, __construct); +PHP_METHOD(UnknownIdentifierException, __construct); + +PHP_METHOD(Pimple, __construct); +PHP_METHOD(Pimple, factory); +PHP_METHOD(Pimple, protect); +PHP_METHOD(Pimple, raw); +PHP_METHOD(Pimple, extend); +PHP_METHOD(Pimple, keys); +PHP_METHOD(Pimple, register); +PHP_METHOD(Pimple, offsetSet); +PHP_METHOD(Pimple, offsetUnset); +PHP_METHOD(Pimple, offsetGet); +PHP_METHOD(Pimple, offsetExists); + +PHP_METHOD(PimpleClosure, invoker); + +typedef struct _pimple_bucket_value { + zval *value; /* Must be the first element */ + zval *raw; + zend_object_handle handle_num; + enum { + PIMPLE_IS_PARAM = 0, + PIMPLE_IS_SERVICE = 2 + } type; + zend_bool initialized; + zend_fcall_info_cache fcc; +} pimple_bucket_value; + +typedef struct _pimple_object { + zend_object zobj; + HashTable values; + HashTable factories; + HashTable protected; +} pimple_object; + +typedef struct _pimple_closure_object { + zend_object zobj; + zval *callable; + zval *factory; +} pimple_closure_object; + +static const char sensiolabs_logo[] = ""; + +static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC); + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); + +static void pimple_bucket_dtor(pimple_bucket_value *bucket); +static void pimple_free_bucket(pimple_bucket_value *bucket); + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC); +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC); +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC); +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC); + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC); +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC); +static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC); +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC); + +#ifdef ZTS +#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v) +#else +#define PIMPLE_G(v) (pimple_globals.v) +#endif + +#endif /* PHP_PIMPLE_H */ + diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple.c b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple.c new file mode 100644 index 0000000..c80499b --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple.c @@ -0,0 +1,1114 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_pimple.h" +#include "pimple_compat.h" +#include "zend_interfaces.h" +#include "zend.h" +#include "Zend/zend_closures.h" +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" +#include "main/php_output.h" +#include "SAPI.h" + +static zend_class_entry *pimple_ce_PsrContainerInterface; +static zend_class_entry *pimple_ce_PsrContainerExceptionInterface; +static zend_class_entry *pimple_ce_PsrNotFoundExceptionInterface; + +static zend_class_entry *pimple_ce_ExpectedInvokableException; +static zend_class_entry *pimple_ce_FrozenServiceException; +static zend_class_entry *pimple_ce_InvalidServiceIdentifierException; +static zend_class_entry *pimple_ce_UnknownIdentifierException; + +static zend_class_entry *pimple_ce; +static zend_object_handlers pimple_object_handlers; +static zend_class_entry *pimple_closure_ce; +static zend_class_entry *pimple_serviceprovider_ce; +static zend_object_handlers pimple_closure_object_handlers; +static zend_internal_function pimple_closure_invoker_function; + +#define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \ + ulong index; \ + pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \ + +#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS do { \ + if (ce != pimple_ce) { \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \ + if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + } \ + } else { \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + }\ + } while(0); + +#define PIMPLE_CALL_CB do { \ + zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \ + fci.size = sizeof(fci); \ + fci.object_ptr = retval->fcc.object_ptr; \ + fci.function_name = retval->value; \ + fci.no_separation = 1; \ + fci.retval_ptr_ptr = &retval_ptr_ptr; \ +\ + zend_call_function(&fci, &retval->fcc TSRMLS_CC); \ + efree(fci.params); \ + if (EG(exception)) { \ + return EG(uninitialized_zval_ptr); \ + } \ + } while(0); + + +/* Psr\Container\ContainerInterface */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_get, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_has, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_PsrContainerInterface_functions[] = { + PHP_ABSTRACT_ME(ContainerInterface, get, arginfo_pimple_PsrContainerInterface_get) + PHP_ABSTRACT_ME(ContainerInterface, has, arginfo_pimple_PsrContainerInterface_has) + PHP_FE_END +}; + +/* Psr\Container\ContainerExceptionInterface */ +static const zend_function_entry pimple_ce_PsrContainerExceptionInterface_functions[] = { + PHP_FE_END +}; + +/* Psr\Container\NotFoundExceptionInterface */ +static const zend_function_entry pimple_ce_PsrNotFoundExceptionInterface_functions[] = { + PHP_FE_END +}; + +/* Pimple\Exception\FrozenServiceException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_FrozenServiceException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_FrozenServiceException_functions[] = { + PHP_ME(FrozenServiceException, __construct, arginfo_FrozenServiceException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Exception\InvalidServiceIdentifierException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_InvalidServiceIdentifierException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_InvalidServiceIdentifierException_functions[] = { + PHP_ME(InvalidServiceIdentifierException, __construct, arginfo_InvalidServiceIdentifierException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Exception\UnknownIdentifierException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_UnknownIdentifierException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_UnknownIdentifierException_functions[] = { + PHP_ME(UnknownIdentifierException, __construct, arginfo_UnknownIdentifierException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Container */ +ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) +ZEND_ARG_ARRAY_INFO(0, value, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2) +ZEND_ARG_INFO(0, offset) +ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2) +ZEND_ARG_INFO(0, id) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0) +ZEND_ARG_ARRAY_INFO(0, values, 1) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_functions[] = { + PHP_ME(Pimple, __construct, arginfo___construct, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, factory, arginfo_factory, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, protect, arginfo_protect, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, raw, arginfo_raw, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, extend, arginfo_extend, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, register, arginfo_register, ZEND_ACC_PUBLIC) + + PHP_ME(Pimple, offsetSet, arginfo_offsetset, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetGet, arginfo_offsetget, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetExists, arginfo_offsetexists, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetUnset, arginfo_offsetunset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\ServiceProviderInterface */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = { + PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register) + PHP_FE_END +}; + +/* parent::__construct(sprintf("Something with %s", $arg1)) */ +static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC) +{ + zend_class_entry *ce = Z_OBJCE_P(this_ptr); + char *message = NULL; + int message_len; + zval *constructor_arg; + + message_len = spprintf(&message, 0, format, arg1); + ALLOC_INIT_ZVAL(constructor_arg); + ZVAL_STRINGL(constructor_arg, message, message_len, 1); + + zend_call_method_with_1_params(&this_ptr, ce, &ce->parent->constructor, "__construct", NULL, constructor_arg); + + efree(message); + zval_ptr_dtor(&constructor_arg); +} + +/** + * Pass a single string parameter to exception constructor and throw + */ +static void pimple_throw_exception_string(zend_class_entry *ce, const char *message, zend_uint message_len TSRMLS_DC) +{ + zval *exception, *param; + + ALLOC_INIT_ZVAL(exception); + object_init_ex(exception, ce); + + ALLOC_INIT_ZVAL(param); + ZVAL_STRINGL(param, message, message_len, 1); + + zend_call_method_with_1_params(&exception, ce, &ce->constructor, "__construct", NULL, param); + + zend_throw_exception_object(exception TSRMLS_CC); + + zval_ptr_dtor(¶m); +} + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC) +{ + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + if (obj->factory) { + zval_ptr_dtor(&obj->factory); + } + if (obj->callable) { + zval_ptr_dtor(&obj->callable); + } + efree(obj); +} + +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC) +{ + zend_hash_destroy(&obj->factories); + zend_hash_destroy(&obj->protected); + zend_hash_destroy(&obj->values); + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + efree(obj); +} + +static void pimple_free_bucket(pimple_bucket_value *bucket) +{ + if (bucket->raw) { + zval_ptr_dtor(&bucket->raw); + } +} + +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_closure_object *pimple_closure_obj = NULL; + + pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object)); + ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce); + + pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor; + retval.handlers = &pimple_closure_object_handlers; + retval.handle = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC); + + return retval; +} + +static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC) +{ + zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated"); + + return NULL; +} + +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) +{ + *zobj_ptr = obj; + *ce_ptr = Z_OBJCE_P(obj); + *fptr_ptr = (zend_function *)&pimple_closure_invoker_function; + + return SUCCESS; +} + +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_object *pimple_obj = NULL; + zend_function *function = NULL; + + pimple_obj = emalloc(sizeof(pimple_object)); + ZEND_OBJ_INIT(&pimple_obj->zobj, ce); + + PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS + + retval.handlers = &pimple_object_handlers; + retval.handle = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC); + + zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + + return retval; +} + +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value pimple_value = {0}, *found_value = NULL; + ulong hash; + + pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC); + + if (!offset) {/* $p[] = 'foo' when not overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + return; + } + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + return; + } + if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + return; + } + if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_NULL: /* $p[] = 'foo' when overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + break; + default: + pimple_free_bucket(&pimple_value); + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_del(&pimple_obj->values, index); + zend_hash_index_del(&pimple_obj->factories, index); + zend_hash_index_del(&pimple_obj->protected, index); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return 0; + } +} + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + zend_fcall_info fci = {0}; + zval *retval_ptr_ptr = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + + return EG(uninitialized_zval_ptr); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) { + return EG(uninitialized_zval_ptr); + } + break; + case IS_NULL: /* $p[][3] = 'foo' first dim access */ + return EG(uninitialized_zval_ptr); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return EG(uninitialized_zval_ptr); + } + + if(retval->type == PIMPLE_IS_PARAM) { + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) { + /* Service is protected, return the value every time */ + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) { + /* Service is a factory, call it every time and never cache its result */ + PIMPLE_CALL_CB + Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */ + return retval_ptr_ptr; + } + + if (retval->initialized == 1) { + /* Service has already been called, return its cached value */ + return retval->value; + } + + ALLOC_INIT_ZVAL(retval->raw); + MAKE_COPY_ZVAL(&retval->value, retval->raw); + + PIMPLE_CALL_CB + + retval->initialized = 1; + zval_ptr_dtor(&retval->value); + retval->value = retval_ptr_ptr; + + return retval->value; +} + +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return FAILURE; + } + + if (_pimple_bucket_value->fcc.called_scope) { + return SUCCESS; + } + + if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope; + return SUCCESS; + } else { + return FAILURE; + } +} + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + _pimple_bucket_value->value = _zval; + + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return PIMPLE_IS_PARAM; + } + + if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->type = PIMPLE_IS_SERVICE; + _pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval); + } + + return PIMPLE_IS_SERVICE; +} + +static void pimple_bucket_dtor(pimple_bucket_value *bucket) +{ + zval_ptr_dtor(&bucket->value); + pimple_free_bucket(bucket); +} + +PHP_METHOD(FrozenServiceException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Cannot override frozen service \"%s\".", id TSRMLS_CC); +} + +PHP_METHOD(InvalidServiceIdentifierException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" does not contain an object definition.", id TSRMLS_CC); +} + +PHP_METHOD(UnknownIdentifierException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" is not defined.", id TSRMLS_CC); +} + +PHP_METHOD(Pimple, protect) +{ + zval *protected = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(protected); + RETURN_ZVAL(protected, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + RETURN_FALSE; +} + +PHP_METHOD(Pimple, raw) +{ + zval *offset = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value *value = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (value->raw) { + RETVAL_ZVAL(value->raw, 1, 0); + } else { + RETVAL_ZVAL(value->value, 1, 0); + } +} + +PHP_METHOD(Pimple, extend) +{ + zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL; + pimple_bucket_value bucket = {0}, *value = NULL; + pimple_object *pobj = NULL; + pimple_closure_object *pcobj = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + + if (value->type != PIMPLE_IS_SERVICE) { + pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (zend_hash_index_exists(&pobj->protected, value->handle_num)) { + int er = EG(error_reporting); + EG(error_reporting) = 0; + php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%s\" should be protected?", Z_STRVAL_P(offset)); + EG(error_reporting) = er; + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (zend_hash_index_exists(&pobj->protected, value->handle_num)) { + int er = EG(error_reporting); + EG(error_reporting) = 0; + php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%ld\" should be protected?", index); + EG(error_reporting) = er; + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + RETURN_NULL(); + } + pimple_free_bucket(&bucket); + + ALLOC_INIT_ZVAL(pimple_closure_obj); + object_init_ex(pimple_closure_obj, pimple_closure_ce); + + pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC); + pcobj->callable = callable; + pcobj->factory = value->value; + Z_ADDREF_P(callable); + Z_ADDREF_P(value->value); + + if (zend_hash_index_exists(&pobj->factories, value->handle_num)) { + pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC); + zend_hash_index_del(&pobj->factories, value->handle_num); + zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(pimple_closure_obj); + } + + pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC); + + RETVAL_ZVAL(pimple_closure_obj, 1, 1); +} + +PHP_METHOD(Pimple, keys) +{ + HashPosition pos; + pimple_object *pobj = NULL; + zval **value = NULL; + zval *endval = NULL; + char *str_index = NULL; + int str_len; + ulong num_index; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + array_init_size(return_value, zend_hash_num_elements(&pobj->values)); + + zend_hash_internal_pointer_reset_ex(&pobj->values, &pos); + + while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) { + MAKE_STD_ZVAL(endval); + switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) { + case HASH_KEY_IS_STRING: + ZVAL_STRINGL(endval, str_index, str_len - 1, 1); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + case HASH_KEY_IS_LONG: + ZVAL_LONG(endval, num_index); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + } + zend_hash_move_forward_ex(&pobj->values, &pos); + } +} + +PHP_METHOD(Pimple, factory) +{ + zval *factory = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(factory); + RETURN_ZVAL(factory, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + + RETURN_FALSE; +} + +PHP_METHOD(Pimple, offsetSet) +{ + zval *offset = NULL, *value = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) { + return; + } + + pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetGet) +{ + zval *offset = NULL, *retval = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC); + + RETVAL_ZVAL(retval, 1, 0); +} + +PHP_METHOD(Pimple, offsetUnset) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pimple_object_unset_dimension(getThis(), offset TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetExists) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC)); +} + +PHP_METHOD(Pimple, register) +{ + zval *provider; + zval **data; + zval *retval = NULL; + zval key; + + HashTable *array = NULL; + HashPosition pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) { + return; + } + + RETVAL_ZVAL(getThis(), 1, 0); + + zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis()); + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (!array) { + return; + } + + zend_hash_internal_pointer_reset_ex(array, &pos); + + while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) { + zend_hash_get_current_key_zval_ex(array, &key, &pos); + pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC); + zend_hash_move_forward_ex(array, &pos); + } +} + +PHP_METHOD(Pimple, __construct) +{ + zval *values = NULL, **pData = NULL, offset; + HashPosition pos; + char *str_index = NULL; + zend_uint str_length; + ulong num_index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE) { + return; + } + + PIMPLE_DEPRECATE + + if (!values) { + return; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos); + while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) { + zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos); + zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos); + INIT_ZVAL(offset); + if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) { + ZVAL_LONG(&offset, num_index); + } else { + ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0); + } + pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC); + zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos); + } +} + +/* + * This is PHP code snippet handling extend()s calls : + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + */ +PHP_METHOD(PimpleClosure, invoker) +{ + pimple_closure_object *pcobj = NULL; + zval *arg = NULL, *retval = NULL, *newretval = NULL; + zend_fcall_info fci = {0}; + zval **args[2]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { + return; + } + + pcobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + fci.function_name = pcobj->factory; + args[0] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 1, args); + fci.retval_ptr_ptr = &retval; + fci.size = sizeof(fci); + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + return; /* Should here return default zval */ + } + + efree(fci.params); + memset(&fci, 0, sizeof(fci)); + fci.size = sizeof(fci); + + fci.function_name = pcobj->callable; + args[0] = &retval; + args[1] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 2, args); + fci.retval_ptr_ptr = &newretval; + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + zval_ptr_dtor(&retval); + return; + } + + efree(fci.params); + zval_ptr_dtor(&retval); + + RETVAL_ZVAL(newretval, 1 ,1); +} + +PHP_MINIT_FUNCTION(pimple) +{ + zend_class_entry tmp_ce_PsrContainerInterface, tmp_ce_PsrContainerExceptionInterface, tmp_ce_PsrNotFoundExceptionInterface; + zend_class_entry tmp_ce_ExpectedInvokableException, tmp_ce_FrozenServiceException, tmp_ce_InvalidServiceIdentifierException, tmp_ce_UnknownIdentifierException; + zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce; + + /* Psr\Container namespace */ + INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerInterface, PSR_CONTAINER_NS, "ContainerInterface", pimple_ce_PsrContainerInterface_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerExceptionInterface, PSR_CONTAINER_NS, "ContainerExceptionInterface", pimple_ce_PsrContainerExceptionInterface_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_PsrNotFoundExceptionInterface, PSR_CONTAINER_NS, "NotFoundExceptionInterface", pimple_ce_PsrNotFoundExceptionInterface_functions); + + pimple_ce_PsrContainerInterface = zend_register_internal_interface(&tmp_ce_PsrContainerInterface TSRMLS_CC); + pimple_ce_PsrContainerExceptionInterface = zend_register_internal_interface(&tmp_ce_PsrContainerExceptionInterface TSRMLS_CC); + pimple_ce_PsrNotFoundExceptionInterface = zend_register_internal_interface(&tmp_ce_PsrNotFoundExceptionInterface TSRMLS_CC); + + zend_class_implements(pimple_ce_PsrNotFoundExceptionInterface TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + + /* Pimple\Exception namespace */ + INIT_NS_CLASS_ENTRY(tmp_ce_ExpectedInvokableException, PIMPLE_EXCEPTION_NS, "ExpectedInvokableException", NULL); + INIT_NS_CLASS_ENTRY(tmp_ce_FrozenServiceException, PIMPLE_EXCEPTION_NS, "FrozenServiceException", pimple_ce_FrozenServiceException_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_InvalidServiceIdentifierException, PIMPLE_EXCEPTION_NS, "InvalidServiceIdentifierException", pimple_ce_InvalidServiceIdentifierException_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_UnknownIdentifierException, PIMPLE_EXCEPTION_NS, "UnknownIdentifierException", pimple_ce_UnknownIdentifierException_functions); + + pimple_ce_ExpectedInvokableException = zend_register_internal_class_ex(&tmp_ce_ExpectedInvokableException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + pimple_ce_FrozenServiceException = zend_register_internal_class_ex(&tmp_ce_FrozenServiceException, spl_ce_RuntimeException, NULL TSRMLS_CC); + pimple_ce_InvalidServiceIdentifierException = zend_register_internal_class_ex(&tmp_ce_InvalidServiceIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + pimple_ce_UnknownIdentifierException = zend_register_internal_class_ex(&tmp_ce_UnknownIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + + zend_class_implements(pimple_ce_ExpectedInvokableException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_FrozenServiceException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_InvalidServiceIdentifierException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_UnknownIdentifierException TSRMLS_CC, 1, pimple_ce_PsrNotFoundExceptionInterface); + + /* Pimple namespace */ + INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions); + INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL); + INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions); + + tmp_pimple_ce.create_object = pimple_object_create; + tmp_pimple_closure_ce.create_object = pimple_closure_object_create; + + pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC); + zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess); + + pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC); + pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + + pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC); + + memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers())); + pimple_object_handlers = std_object_handlers; + pimple_closure_object_handlers.get_closure = pimple_closure_get_closure; + + pimple_closure_invoker_function.function_name = "Pimple closure internal invoker"; + pimple_closure_invoker_function.fn_flags |= ZEND_ACC_CLOSURE; + pimple_closure_invoker_function.handler = ZEND_MN(PimpleClosure_invoker); + pimple_closure_invoker_function.num_args = 1; + pimple_closure_invoker_function.required_num_args = 1; + pimple_closure_invoker_function.scope = pimple_closure_ce; + pimple_closure_invoker_function.type = ZEND_INTERNAL_FUNCTION; + pimple_closure_invoker_function.module = &pimple_module_entry; + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(pimple) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled"); + php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION); + php_info_print_table_end(); + + php_info_print_box_start(0); + php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC); + if (!sapi_module.phpinfo_as_text) { + php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC); + } + php_info_print_box_end(); +} + +zend_module_entry pimple_module_entry = { + STANDARD_MODULE_HEADER, + "pimple", + NULL, + PHP_MINIT(pimple), + NULL, + NULL, + NULL, + PHP_MINFO(pimple), + PIMPLE_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_PIMPLE +ZEND_GET_MODULE(pimple) +#endif diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple_compat.h b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple_compat.h new file mode 100644 index 0000000..d234e17 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/pimple_compat.h @@ -0,0 +1,81 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PIMPLE_COMPAT_H_ +#define PIMPLE_COMPAT_H_ + +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 +#define PHP_5_5_X_API_NO 220121212 +#define PHP_5_6_X_API_NO 220131226 + +#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO +#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO + +#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO +#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO + +#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO +#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO +#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + +#if IS_PHP_53 +#define object_properties_init(obj, ce) do { \ + zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \ + } while (0); +#endif + +#define ZEND_OBJ_INIT(obj, ce) do { \ + zend_object_std_init(obj, ce TSRMLS_CC); \ + object_properties_init((obj), (ce)); \ + } while(0); + +#if IS_PHP_53 || IS_PHP_54 +static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + if (!p) { + Z_TYPE_P(key) = IS_NULL; + } else if (p->nKeyLength) { + Z_TYPE_P(key) = IS_STRING; + Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1); + Z_STRLEN_P(key) = p->nKeyLength - 1; + } else { + Z_TYPE_P(key) = IS_LONG; + Z_LVAL_P(key) = p->h; + } +} +#endif + +#endif /* PIMPLE_COMPAT_H_ */ diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/001.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/001.phpt new file mode 100644 index 0000000..0809ea2 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/001.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + + +--EXPECTF-- +foo +42 +foo2 +foo99 +baz +strstr \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/002.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/002.phpt new file mode 100644 index 0000000..7b56d2c --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test for constructor +--SKIPIF-- + +--FILE-- +'foo')); +var_dump($p[42]); +?> +--EXPECT-- +NULL +string(3) "foo" diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/003.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/003.phpt new file mode 100644 index 0000000..a22cfa3 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test empty dimensions +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +string(3) "bar" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/004.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/004.phpt new file mode 100644 index 0000000..1e1d251 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/004.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test has/unset dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +NULL +bool(true) +bool(false) +bool(true) +bool(true) \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/005.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/005.phpt new file mode 100644 index 0000000..0479ee0 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/005.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test simple class inheritance +--SKIPIF-- + +--FILE-- +someAttr; +?> +--EXPECT-- +string(3) "hit" +foo +fooAttr \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/006.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/006.phpt new file mode 100644 index 0000000..cfe8a11 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/006.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test complex class inheritance +--SKIPIF-- + +--FILE-- + 'bar', 88 => 'baz'); + +$p = new TestPimple($defaultValues); +$p[42] = 'foo'; +var_dump($p[42]); +var_dump($p[0]); +?> +--EXPECT-- +string(13) "hit offsetset" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "foo" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "baz" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/007.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/007.phpt new file mode 100644 index 0000000..5aac683 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/007.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +foo +42 \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/008.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/008.phpt new file mode 100644 index 0000000..db7eeec --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/008.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test frozen services +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/009.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/009.phpt new file mode 100644 index 0000000..bb05ea2 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/009.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test service is called as callback, and only once +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/010.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/010.phpt new file mode 100644 index 0000000..badce01 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/010.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test service is called as callback for every callback type +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +callme +called +Foo::bar +array(2) { + [0]=> + string(3) "Foo" + [1]=> + string(3) "bar" +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/011.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/011.phpt new file mode 100644 index 0000000..6682ab8 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/011.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test service callback throwing an exception +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +all right! \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/012.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/012.phpt new file mode 100644 index 0000000..4c6ac48 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/012.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test service factory +--SKIPIF-- + +--FILE-- +factory($f = function() { var_dump('called-1'); return 'ret-1';}); + +$p[] = $f; + +$p[] = function () { var_dump('called-2'); return 'ret-2'; }; + +var_dump($p[0]); +var_dump($p[0]); +var_dump($p[1]); +var_dump($p[1]); +?> +--EXPECTF-- +string(8) "called-1" +string(5) "ret-1" +string(8) "called-1" +string(5) "ret-1" +string(8) "called-2" +string(5) "ret-2" +string(5) "ret-2" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/013.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/013.phpt new file mode 100644 index 0000000..f419958 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/013.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test keys() +--SKIPIF-- + +--FILE-- +keys()); + +$p['foo'] = 'bar'; +$p[] = 'foo'; + +var_dump($p->keys()); + +unset($p['foo']); + +var_dump($p->keys()); +?> +--EXPECTF-- +array(0) { +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(0) +} +array(1) { + [0]=> + int(0) +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/014.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/014.phpt new file mode 100644 index 0000000..ac93721 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/014.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test raw() +--SKIPIF-- + +--FILE-- +raw('foo')); +var_dump($p[42]); + +unset($p['foo']); + +try { + $p->raw('foo'); + echo "expected exception"; +} catch (InvalidArgumentException $e) { } +--EXPECTF-- +string(8) "called-2" +string(5) "ret-2" +object(Closure)#%i (0) { +} +string(8) "called-2" +string(5) "ret-2" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/015.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/015.phpt new file mode 100644 index 0000000..314f008 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/015.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test protect() +--SKIPIF-- + +--FILE-- +protect($f); + +var_dump($p['foo']); +--EXPECTF-- +object(Closure)#%i (0) { +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/016.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/016.phpt new file mode 100644 index 0000000..e55edb0 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/016.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test extend() +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */ + +var_dump($c('param')); +--EXPECTF-- +string(5) "param" +string(3) "foo" +string(3) "bar" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017.phpt new file mode 100644 index 0000000..bac23ce --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service extension +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { throw new BadMethodCallException; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt new file mode 100644 index 0000000..8f881d6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service factory +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { return 'foobar'; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/018.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/018.phpt new file mode 100644 index 0000000..27c12a1 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/018.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test register() +--SKIPIF-- + +--FILE-- +register(new Foo, array(42 => 'bar')); + +var_dump($p[42]); +--EXPECTF-- +object(Pimple\Container)#1 (0) { +} +string(3) "bar" \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/019.phpt b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/019.phpt new file mode 100644 index 0000000..28a9aec --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/019.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test register() returns static and is a fluent interface +--SKIPIF-- + +--FILE-- +register(new Foo)); +--EXPECTF-- +bool(true) diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench.phpb b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench.phpb new file mode 100644 index 0000000..8f983e6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench.phpb @@ -0,0 +1,51 @@ +factory($factory); + +$p['factory'] = $factory; + +echo $p['factory']; +echo $p['factory']; +echo $p['factory']; + +} + +echo microtime(true) - $time; diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb new file mode 100644 index 0000000..aec541f --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb @@ -0,0 +1,25 @@ + diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/phpunit.xml.dist b/addons/weliam_smartcity/vendor/pimple/pimple/phpunit.xml.dist new file mode 100644 index 0000000..5c8d487 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./src/Pimple/Tests + + + diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Container.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Container.php new file mode 100644 index 0000000..707b92b --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Container.php @@ -0,0 +1,298 @@ +factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * + * @throws FrozenServiceException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if ( + isset($this->raw[$id]) + || !\is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !\method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (\is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function factory($callable) + { + if (!\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function protect($callable) + { + if (!\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws UnknownIdentifierException If the identifier is not defined + * @throws FrozenServiceException If the service is frozen + * @throws InvalidServiceIdentifierException If the identifier belongs to a parameter + * @throws ExpectedInvokableException If the extension callable is not a closure or an invokable object + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + if (!\is_object($this->values[$id]) || !\method_exists($this->values[$id], '__invoke')) { + throw new InvalidServiceIdentifierException($id); + } + + if (isset($this->protected[$this->values[$id]])) { + @\trigger_error(\sprintf('How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?', $id), \E_USER_DEPRECATED); + } + + if (!\is_object($callable) || !\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return \array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + + return $this; + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php new file mode 100644 index 0000000..7228421 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php @@ -0,0 +1,38 @@ + + */ +class ExpectedInvokableException extends \InvalidArgumentException implements ContainerExceptionInterface +{ +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php new file mode 100644 index 0000000..e4d2f6d --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php @@ -0,0 +1,45 @@ + + */ +class FrozenServiceException extends \RuntimeException implements ContainerExceptionInterface +{ + /** + * @param string $id Identifier of the frozen service + */ + public function __construct($id) + { + parent::__construct(\sprintf('Cannot override frozen service "%s".', $id)); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php new file mode 100644 index 0000000..91e82f9 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php @@ -0,0 +1,45 @@ + + */ +class InvalidServiceIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface +{ + /** + * @param string $id The invalid identifier + */ + public function __construct($id) + { + parent::__construct(\sprintf('Identifier "%s" does not contain an object definition.', $id)); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php new file mode 100644 index 0000000..fb6b626 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php @@ -0,0 +1,45 @@ + + */ +class UnknownIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface +{ + /** + * @param string $id The unknown identifier + */ + public function __construct($id) + { + parent::__construct(\sprintf('Identifier "%s" is not defined.', $id)); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/Container.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/Container.php new file mode 100644 index 0000000..cadbfff --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/Container.php @@ -0,0 +1,55 @@ + + */ +final class Container implements ContainerInterface +{ + private $pimple; + + public function __construct(PimpleContainer $pimple) + { + $this->pimple = $pimple; + } + + public function get($id) + { + return $this->pimple[$id]; + } + + public function has($id) + { + return isset($this->pimple[$id]); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php new file mode 100644 index 0000000..3361c6f --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php @@ -0,0 +1,75 @@ + + */ +class ServiceLocator implements ContainerInterface +{ + private $container; + private $aliases = array(); + + /** + * @param PimpleContainer $container The Container instance used to locate services + * @param array $ids Array of service ids that can be located. String keys can be used to define aliases + */ + public function __construct(PimpleContainer $container, array $ids) + { + $this->container = $container; + + foreach ($ids as $key => $id) { + $this->aliases[\is_int($key) ? $id : $key] = $id; + } + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + if (!isset($this->aliases[$id])) { + throw new UnknownIdentifierException($id); + } + + return $this->container[$this->aliases[$id]]; + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + return isset($this->aliases[$id]) && isset($this->container[$this->aliases[$id]]); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceIterator.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceIterator.php new file mode 100644 index 0000000..5cde518 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceIterator.php @@ -0,0 +1,69 @@ + + */ +final class ServiceIterator implements \Iterator +{ + private $container; + private $ids; + + public function __construct(Container $container, array $ids) + { + $this->container = $container; + $this->ids = $ids; + } + + public function rewind() + { + \reset($this->ids); + } + + public function current() + { + return $this->container[\current($this->ids)]; + } + + public function key() + { + return \current($this->ids); + } + + public function next() + { + \next($this->ids); + } + + public function valid() + { + return null !== \key($this->ids); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php new file mode 100644 index 0000000..c004594 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php @@ -0,0 +1,46 @@ +value = $value; + + return $service; + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php new file mode 100644 index 0000000..33cd4e5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php @@ -0,0 +1,34 @@ +factory(function () { + return new Service(); + }); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php new file mode 100644 index 0000000..d71b184 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php @@ -0,0 +1,35 @@ + + */ +class Service +{ + public $value; +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php new file mode 100644 index 0000000..8e5c4c7 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php @@ -0,0 +1,76 @@ + + */ +class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase +{ + public function testProvider() + { + $pimple = new Container(); + + $pimpleServiceProvider = new Fixtures\PimpleServiceProvider(); + $pimpleServiceProvider->register($pimple); + + $this->assertEquals('value', $pimple['param']); + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testProviderWithRegisterMethod() + { + $pimple = new Container(); + + $pimple->register(new Fixtures\PimpleServiceProvider(), array( + 'anotherParameter' => 'anotherValue', + )); + + $this->assertEquals('value', $pimple['param']); + $this->assertEquals('anotherValue', $pimple['anotherParameter']); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php new file mode 100644 index 0000000..acb66e0 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php @@ -0,0 +1,589 @@ + + */ +class PimpleTest extends \PHPUnit_Framework_TestCase +{ + public function testWithString() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + + $this->assertEquals('value', $pimple['param']); + } + + public function testWithClosure() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + } + + public function testServicesShouldBeDifferent() + { + $pimple = new Container(); + $pimple['service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $serviceOne = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testShouldPassContainerAsParameter() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $pimple['container'] = function ($container) { + return $container; + }; + + $this->assertNotSame($pimple, $pimple['service']); + $this->assertSame($pimple, $pimple['container']); + } + + public function testIsset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $pimple['null'] = null; + + $this->assertTrue(isset($pimple['param'])); + $this->assertTrue(isset($pimple['service'])); + $this->assertTrue(isset($pimple['null'])); + $this->assertFalse(isset($pimple['non_existent'])); + } + + public function testConstructorInjection() + { + $params = array('param' => 'value'); + $pimple = new Container($params); + + $this->assertSame($params['param'], $pimple['param']); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + public function testOffsetGetHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple['foo']); + } + + public function testUnset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + unset($pimple['param'], $pimple['service']); + $this->assertFalse(isset($pimple['param'])); + $this->assertFalse(isset($pimple['service'])); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testShare($service) + { + $pimple = new Container(); + $pimple['shared_service'] = $service; + + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertSame($serviceOne, $serviceTwo); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testProtect($service) + { + $pimple = new Container(); + $pimple['protected'] = $pimple->protect($service); + + $this->assertSame($service, $pimple['protected']); + } + + public function testGlobalFunctionNameAsParameterValue() + { + $pimple = new Container(); + $pimple['global_function'] = 'strlen'; + $this->assertSame('strlen', $pimple['global_function']); + } + + public function testRaw() + { + $pimple = new Container(); + $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; }); + $this->assertSame($definition, $pimple->raw('service')); + } + + public function testRawHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple->raw('foo')); + } + + public function testFluentRegister() + { + $pimple = new Container(); + $this->assertSame($pimple, $pimple->register($this->getMockBuilder('Pimple\ServiceProviderInterface')->getMock())); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testExtend($service) + { + $pimple = new Container(); + $pimple['shared_service'] = function () { + return new Fixtures\Service(); + }; + $pimple['factory_service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $pimple->extend('shared_service', $service); + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertSame($serviceOne, $serviceTwo); + $this->assertSame($serviceOne->value, $serviceTwo->value); + + $pimple->extend('factory_service', $service); + $serviceOne = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertNotSame($serviceOne, $serviceTwo); + $this->assertNotSame($serviceOne->value, $serviceTwo->value); + } + + public function testExtendDoesNotLeakWithFactories() + { + if (extension_loaded('pimple')) { + $this->markTestSkipped('Pimple extension does not support this test'); + } + $pimple = new Container(); + + $pimple['foo'] = $pimple->factory(function () { return; }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; }); + unset($pimple['foo']); + + $p = new \ReflectionProperty($pimple, 'values'); + $p->setAccessible(true); + $this->assertEmpty($p->getValue($pimple)); + + $p = new \ReflectionProperty($pimple, 'factories'); + $p->setAccessible(true); + $this->assertCount(0, $p->getValue($pimple)); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + public function testKeys() + { + $pimple = new Container(); + $pimple['foo'] = 123; + $pimple['bar'] = 123; + + $this->assertEquals(array('foo', 'bar'), $pimple->keys()); + } + + /** @test */ + public function settingAnInvokableObjectShouldTreatItAsFactory() + { + $pimple = new Container(); + $pimple['invokable'] = new Fixtures\Invokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']); + } + + /** @test */ + public function settingNonInvokableObjectShouldTreatItAsParameter() + { + $pimple = new Container(); + $pimple['non_invokable'] = new Fixtures\NonInvokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testLegacyFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testLegacyProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\InvalidServiceIdentifierException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testLegacyExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @expectedDeprecation How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "foo" should be protected? + */ + public function testExtendingProtectedClosureDeprecation() + { + $pimple = new Container(); + $pimple['foo'] = $pimple->protect(function () { + return 'bar'; + }); + + $pimple->extend('foo', function ($value) { + return $value.'-baz'; + }); + + $this->assertSame('bar-baz', $pimple['foo']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testLegacyExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testExtendFailsIfFrozenServiceIsNonInvokable() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return new Fixtures\NonInvokable(); + }; + $foo = $pimple['foo']; + + $pimple->extend('foo', function () {}); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testExtendFailsIfFrozenServiceIsInvokable() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return new Fixtures\Invokable(); + }; + $foo = $pimple['foo']; + + $pimple->extend('foo', function () {}); + } + + /** + * Provider for invalid service definitions. + */ + public function badServiceDefinitionProvider() + { + return array( + array(123), + array(new Fixtures\NonInvokable()), + ); + } + + /** + * Provider for service definitions. + */ + public function serviceDefinitionProvider() + { + return array( + array(function ($value) { + $service = new Fixtures\Service(); + $service->value = $value; + + return $service; + }), + array(new Fixtures\Invokable()), + ); + } + + public function testDefiningNewServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['bar']); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + /** + * @group legacy + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testLegacyOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + public function testRemovingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + unset($pimple['foo']); + $pimple['foo'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['foo']); + } + + public function testExtendingService() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.bar"; + }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.baz"; + }); + $this->assertSame('foo.bar.baz', $pimple['foo']); + } + + public function testExtendingServiceAfterOtherServiceFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['bar'] = function () { + return 'bar'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) { + return "$bar.baz"; + }); + $this->assertSame('bar.baz', $pimple['bar']); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php new file mode 100644 index 0000000..7ca2d7f --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php @@ -0,0 +1,77 @@ +assertSame($pimple['service'], $psr->get('service')); + } + + /** + * @expectedException \Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage Identifier "service" is not defined. + */ + public function testGetThrowsExceptionIfServiceIsNotFound() + { + $pimple = new Container(); + $psr = new PsrContainer($pimple); + + $psr->get('service'); + } + + public function testHasReturnsTrueIfServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Service(); + }; + $psr = new PsrContainer($pimple); + + $this->assertTrue($psr->has('service')); + } + + public function testHasReturnsFalseIfServiceDoesNotExist() + { + $pimple = new Container(); + $psr = new PsrContainer($pimple); + + $this->assertFalse($psr->has('service')); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php new file mode 100644 index 0000000..c9a0812 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php @@ -0,0 +1,134 @@ + + */ +class ServiceLocatorTest extends TestCase +{ + public function testCanAccessServices() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('service')); + + $this->assertSame($pimple['service'], $locator->get('service')); + } + + public function testCanAccessAliasedServices() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $this->assertSame($pimple['service'], $locator->get('alias')); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "service" is not defined. + */ + public function testCannotAccessAliasedServiceUsingRealIdentifier() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $service = $locator->get('service'); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testGetValidatesServiceCanBeLocated() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $service = $locator->get('foo'); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "invalid" is not defined. + */ + public function testGetValidatesTargetServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'invalid')); + + $service = $locator->get('alias'); + } + + public function testHasValidatesServiceCanBeLocated() + { + $pimple = new Container(); + $pimple['service1'] = function () { + return new Fixtures\Service(); + }; + $pimple['service2'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('service1')); + + $this->assertTrue($locator->has('service1')); + $this->assertFalse($locator->has('service2')); + } + + public function testHasChecksIfTargetServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('foo' => 'service', 'bar' => 'invalid')); + + $this->assertTrue($locator->has('foo')); + $this->assertFalse($locator->has('bar')); + } +} diff --git a/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php new file mode 100644 index 0000000..5dd52f0 --- /dev/null +++ b/addons/weliam_smartcity/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php @@ -0,0 +1,52 @@ +assertSame(array('service1' => $pimple['service1'], 'service2' => $pimple['service2']), iterator_to_array($iterator)); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/mysql/README.md b/addons/weliam_smartcity/vendor/workerman/mysql/README.md new file mode 100644 index 0000000..a8b3edb --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/mysql/README.md @@ -0,0 +1,78 @@ +# Workerman\Mysql\Connection + +Long-living MySQL connection for daemon. + +# Install +```composer require workerman/mysql``` + +# Usage +```php +$db = new Workerman\MySQL\Connection($mysql_host, $mysql_port, $user, $password, $db_bname); + +// Get all rows. +$db1->select('ID,Sex')->from('Persons')->where('sex= :sex')->bindValues(array('sex'=>'M'))->query(); +// Equivalent to. +$db1->select('ID,Sex')->from('Persons')->where("sex='F'")->query(); +// Equivalent to. +$db->query("SELECT ID,Sex FROM `Persons` WHERE sex='M'"); + + +// Get one row. +$db->select('ID,Sex')->from('Persons')->where('sex= :sex')->bindValues(array('sex'=>'M'))->row(); +// Equivalent to. +$db->select('ID,Sex')->from('Persons')->where("sex= 'F' ")->row(); +// Equivalent to. +$db->row("SELECT ID,Sex FROM `Persons` WHERE sex='M'"); + + +// Get a column. +$db->select('ID')->from('Persons')->where('sex= :sex')->bindValues(array('sex'=>'M'))->column(); +// Equivalent to. +$db->select('ID')->from('Persons')->where("sex= 'F' ")->column(); +// Equivalent to. +$db->column("SELECT `ID` FROM `Persons` WHERE sex='M'"); + +// Get single. +$db->select('ID,Sex')->from('Persons')->where('sex= :sex')->bindValues(array('sex'=>'M'))->single(); +// Equivalent to. +$db->select('ID,Sex')->from('Persons')->where("sex= 'F' ")->single(); +// Equivalent to. +$db->single("SELECT ID,Sex FROM `Persons` WHERE sex='M'"); + +// Complex query. +$db->select('*')->from('table1')->innerJoin('table2','table1.uid = table2.uid')->where('age > :age') +->groupBy(array('aid'))->having('foo="foo"')->orderByASC/*orderByDESC*/(array('did')) +->limit(10)->offset(20)->bindValues(array('age' => 13)); +// Equivalent to. +$db->query(SELECT * FROM `table1` INNER JOIN `table2` ON `table1`.`uid` = `table2`.`uid` WHERE age > 13 +GROUP BY aid HAVING foo="foo" ORDER BY did LIMIT 10 OFFSET 20“); + +// Insert. +$insert_id = $db->insert('Persons')->cols(array( + 'Firstname'=>'abc', + 'Lastname'=>'efg', + 'Sex'=>'M', + 'Age'=>13))->query(); +// Equivalent to. +$insert_id = $db->query("INSERT INTO `Persons` ( `Firstname`,`Lastname`,`Sex`,`Age`) +VALUES ( 'abc', 'efg', 'M', 13)"); + +// Updagte. +$row_count = $db->update('Persons')->cols(array('sex'))->where('ID=1') +->bindValue('sex', 'F')->query(); +// Equivalent to. +$row_count = $db->update('Persons')->cols(array('sex'=>'F'))->where('ID=1')->query(); +// Equivalent to. +$row_count = $db->query("UPDATE `Persons` SET `sex` = 'F' WHERE ID=1"); + +// Delete. +$row_count = $db->delete('Persons')->where('ID=9')->query(); +// Equivalent to. +$row_count = $db->query("DELETE FROM `Persons` WHERE ID=9"); + +// Transaction. +$db1->beginTrans(); +.... +$db1->commitTrans(); // or $db1->rollBackTrans(); + +``` diff --git a/addons/weliam_smartcity/vendor/workerman/mysql/composer.json b/addons/weliam_smartcity/vendor/workerman/mysql/composer.json new file mode 100644 index 0000000..51c0328 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/mysql/composer.json @@ -0,0 +1,16 @@ +{ + "name" : "workerman/mysql", + "type" : "library", + "keywords": ["mysql", "pdo", "pdo_mysql"], + "homepage": "http://www.workerman.net", + "license" : "MIT", + "description": "Long-living MySQL connection for daemon.", + "require": { + "php": ">=5.3", + "ext-pdo": "*", + "ext-pdo_mysql": "*" + }, + "autoload": { + "psr-4": {"Workerman\\MySQL\\": "./src"} + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/mysql/src/Connection.php b/addons/weliam_smartcity/vendor/workerman/mysql/src/Connection.php new file mode 100644 index 0000000..69ff499 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/mysql/src/Connection.php @@ -0,0 +1,1986 @@ +type = 'SELECT'; + if (!is_array($cols)) { + $cols = explode(',', $cols); + } + $this->cols($cols); + return $this; + } + + /** + * 从哪个表删除 + * + * @param string $table + * @return self + */ + public function delete($table) + { + $this->type = 'DELETE'; + $this->table = $this->quoteName($table); + $this->fromRaw($this->quoteName($table)); + return $this; + } + + /** + * 更新哪个表 + * + * @param string $table + * @return self + */ + public function update($table) + { + $this->type = 'UPDATE'; + $this->table = $this->quoteName($table); + return $this; + } + + /** + * 向哪个表插入 + * + * @param string $table + * @return self + */ + public function insert($table) + { + $this->type = 'INSERT'; + $this->table = $this->quoteName($table); + return $this; + } + + /** + * + * 设置 SQL_CALC_FOUND_ROWS 标记. + * + * @param bool $enable + * @return self + */ + public function calcFoundRows($enable = true) + { + $this->setFlag('SQL_CALC_FOUND_ROWS', $enable); + return $this; + } + + /** + * 设置 SQL_CACHE 标记 + * + * @param bool $enable + * @return self + */ + public function cache($enable = true) + { + $this->setFlag('SQL_CACHE', $enable); + return $this; + } + + /** + * 设置 SQL_NO_CACHE 标记 + * + * @param bool $enable + * @return self + */ + public function noCache($enable = true) + { + $this->setFlag('SQL_NO_CACHE', $enable); + return $this; + } + + /** + * 设置 STRAIGHT_JOIN 标记. + * + * @param bool $enable + * @return self + */ + public function straightJoin($enable = true) + { + $this->setFlag('STRAIGHT_JOIN', $enable); + return $this; + } + + /** + * 设置 HIGH_PRIORITY 标记 + * + * @param bool $enable + * @return self + */ + public function highPriority($enable = true) + { + $this->setFlag('HIGH_PRIORITY', $enable); + return $this; + } + + /** + * 设置 SQL_SMALL_RESULT 标记 + * + * @param bool $enable + * @return self + */ + public function smallResult($enable = true) + { + $this->setFlag('SQL_SMALL_RESULT', $enable); + return $this; + } + + /** + * 设置 SQL_BIG_RESULT 标记 + * + * @param bool $enable + * @return self + */ + public function bigResult($enable = true) + { + $this->setFlag('SQL_BIG_RESULT', $enable); + return $this; + } + + /** + * 设置 SQL_BUFFER_RESULT 标记 + * + * @param bool $enable + * @return self + */ + public function bufferResult($enable = true) + { + $this->setFlag('SQL_BUFFER_RESULT', $enable); + return $this; + } + + /** + * 设置 FOR UPDATE 标记 + * + * @param bool $enable + * @return self + */ + public function forUpdate($enable = true) + { + $this->for_update = (bool)$enable; + return $this; + } + + /** + * 设置 DISTINCT 标记 + * + * @param bool $enable + * @return self + */ + public function distinct($enable = true) + { + $this->setFlag('DISTINCT', $enable); + return $this; + } + + /** + * 设置 LOW_PRIORITY 标记 + * + * @param bool $enable + * @return self + */ + public function lowPriority($enable = true) + { + $this->setFlag('LOW_PRIORITY', $enable); + return $this; + } + + /** + * 设置 IGNORE 标记 + * + * @param bool $enable + * @return self + */ + public function ignore($enable = true) + { + $this->setFlag('IGNORE', $enable); + return $this; + } + + /** + * 设置 QUICK 标记 + * + * @param bool $enable + * @return self + */ + public function quick($enable = true) + { + $this->setFlag('QUICK', $enable); + return $this; + } + + /** + * 设置 DELAYED 标记 + * + * @param bool $enable + * @return self + */ + public function delayed($enable = true) + { + $this->setFlag('DELAYED', $enable); + return $this; + } + + /** + * 序列化 + * + * @return string + */ + public function __toString() + { + $union = ''; + if ($this->union) { + $union = implode(' ', $this->union) . ' '; + } + return $union . $this->build(); + } + + /** + * 设置每页多少条记录 + * + * @param int $paging + * @return self + */ + public function setPaging($paging) + { + $this->paging = (int)$paging; + return $this; + } + + /** + * 获取每页多少条记录 + * + * @return int + */ + public function getPaging() + { + return $this->paging; + } + + /** + * 获取绑定在占位符上的值 + */ + public function getBindValues() + { + switch ($this->type) { + case 'SELECT': + return $this->getBindValuesSELECT(); + case 'DELETE': + case 'UPDATE': + case 'INSERT': + return $this->getBindValuesCOMMON(); + default : + throw new Exception("type err"); + } + } + + /** + * 获取绑定在占位符上的值 + * + * @return array + */ + public function getBindValuesSELECT() + { + $bind_values = $this->bind_values; + $i = 1; + foreach ($this->bind_where as $val) { + $bind_values[$i] = $val; + $i++; + } + foreach ($this->bind_having as $val) { + $bind_values[$i] = $val; + $i++; + } + return $bind_values; + } + + /** + * + * SELECT选择哪些列 + * + * @param mixed $key + * @param string $val + * @return void + */ + protected function addColSELECT($key, $val) + { + if (is_string($key)) { + $this->cols[$val] = $key; + } else { + $this->addColWithAlias($val); + } + } + + /** + * SELECT 增加选择的列 + * + * @param string $spec + */ + protected function addColWithAlias($spec) + { + $parts = explode(' ', $spec); + $count = count($parts); + if ($count == 2 && trim($parts[0]) != '' && trim($parts[1]) != '') { + $this->cols[$parts[1]] = $parts[0]; + } elseif ($count == 3 && strtoupper($parts[1]) == 'AS') { + $this->cols[$parts[2]] = $parts[0]; + } else { + $this->cols[] = trim($spec); + } + } + + /** + * from 哪个表 + * + * @param string $table + * @return self + */ + public function from($table) + { + return $this->fromRaw($this->quoteName($table)); + } + + /** + * from的表 + * + * @param string $table + * @return self + */ + public function fromRaw($table) + { + $this->from[] = array($table); + $this->from_key++; + return $this; + } + + /** + * + * 子查询 + * + * @param string $table + * @param string $name The alias name for the sub-select. + * @return self + */ + public function fromSubSelect($table, $name) + { + $this->from[] = array("($table) AS " . $this->quoteName($name)); + $this->from_key++; + return $this; + } + + + /** + * 增加 join 语句 + * + * @param string $table + * @param string $cond + * @param string $type + * @return self + * @throws Exception + */ + public function join($table, $cond = null, $type = '') + { + return $this->joinInternal($type, $table, $cond); + } + + /** + * 增加 join 语句 + * + * @param string $join inner, left, natural + * @param string $table + * @param string $cond + * @return self + * @throws Exception + */ + protected function joinInternal($join, $table, $cond = null) + { + if (!$this->from) { + throw new Exception('Cannot join() without from()'); + } + + $join = strtoupper(ltrim("$join JOIN")); + $table = $this->quoteName($table); + $cond = $this->fixJoinCondition($cond); + $this->from[$this->from_key][] = rtrim("$join $table $cond"); + return $this; + } + + /** + * quote + * + * @param string $cond + * @return string + * + */ + protected function fixJoinCondition($cond) + { + if (!$cond) { + return ''; + } + + $cond = $this->quoteNamesIn($cond); + + if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') { + return $cond; + } + + if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') { + return $cond; + } + + return 'ON ' . $cond; + } + + /** + * inner join + * + * @param string $table + * @param string $cond + * @return self + * @throws Exception + */ + public function innerJoin($table, $cond = null) + { + return $this->joinInternal('INNER', $table, $cond); + } + + /** + * left join + * + * @param string $table + * @param string $cond + * @return self + * @throws Exception + */ + public function leftJoin($table, $cond = null) + { + return $this->joinInternal('LEFT', $table, $cond); + } + + /** + * right join + * + * @param string $table + * @param string $cond + * @return self + * @throws Exception + */ + public function rightJoin($table, $cond = null) + { + return $this->joinInternal('RIGHT', $table, $cond); + } + + /** + * joinSubSelect + * + * @param string $join inner, left, natural + * @param string $spec + * @param string $name sub-select 的别名 + * @param string $cond + * @return self + * @throws Exception + */ + public function joinSubSelect($join, $spec, $name, $cond = null) + { + if (!$this->from) { + throw new \Exception('Cannot join() without from() first.'); + } + + $join = strtoupper(ltrim("$join JOIN")); + $name = $this->quoteName($name); + $cond = $this->fixJoinCondition($cond); + $this->from[$this->from_key][] = rtrim("$join ($spec) AS $name $cond"); + return $this; + } + + /** + * group by 语句 + * + * @param array $cols + * @return self + */ + public function groupBy(array $cols) + { + foreach ($cols as $col) { + $this->group_by[] = $this->quoteNamesIn($col); + } + return $this; + } + + /** + * having 语句 + * + * @param string $cond + * @return self + */ + public function having($cond) + { + $this->addClauseCondWithBind('having', 'AND', func_get_args()); + return $this; + } + + /** + * or having 语句 + * + * @param string $cond The HAVING condition. + * @return self + */ + public function orHaving($cond) + { + $this->addClauseCondWithBind('having', 'OR', func_get_args()); + return $this; + } + + /** + * 设置每页的记录数量 + * + * @param int $page + * @return self + */ + public function page($page) + { + $this->limit = 0; + $this->offset = 0; + + $page = (int)$page; + if ($page > 0) { + $this->limit = $this->paging; + $this->offset = $this->paging * ($page - 1); + } + return $this; + } + + /** + * union + * + * @return self + */ + public function union() + { + $this->union[] = $this->build() . ' UNION'; + $this->reset(); + return $this; + } + + /** + * unionAll + * + * @return self + */ + public function unionAll() + { + $this->union[] = $this->build() . ' UNION ALL'; + $this->reset(); + return $this; + } + + /** + * 重置 + */ + protected function reset() + { + $this->resetFlags(); + $this->cols = array(); + $this->from = array(); + $this->from_key = -1; + $this->where = array(); + $this->group_by = array(); + $this->having = array(); + $this->order_by = array(); + $this->limit = 0; + $this->offset = 0; + $this->for_update = false; + } + + /** + * 清除所有数据 + */ + protected function resetAll() + { + $this->union = array(); + $this->for_update = false; + $this->cols = array(); + $this->from = array(); + $this->from_key = -1; + $this->group_by = array(); + $this->having = array(); + $this->bind_having = array(); + $this->paging = 10; + $this->bind_values = array(); + $this->where = array(); + $this->bind_where = array(); + $this->order_by = array(); + $this->limit = 0; + $this->offset = 0; + $this->flags = array(); + $this->table = ''; + $this->last_insert_id_names = array(); + $this->col_values = array(); + $this->returning = array(); + $this->parameters = array(); + } + + /** + * 创建 SELECT SQL + * + * @return string + */ + protected function buildSELECT() + { + return 'SELECT' + . $this->buildFlags() + . $this->buildCols() + . $this->buildFrom() + . $this->buildWhere() + . $this->buildGroupBy() + . $this->buildHaving() + . $this->buildOrderBy() + . $this->buildLimit() + . $this->buildForUpdate(); + } + + /** + * 创建 DELETE SQL + */ + protected function buildDELETE() + { + return 'DELETE' + . $this->buildFlags() + . $this->buildFrom() + . $this->buildWhere() + . $this->buildOrderBy() + . $this->buildLimit() + . $this->buildReturning(); + } + + /** + * 生成 SELECT 列语句 + * + * @return string + * @throws Exception + */ + protected function buildCols() + { + if (!$this->cols) { + throw new Exception('No columns in the SELECT.'); + } + + $cols = array(); + foreach ($this->cols as $key => $val) { + if (is_int($key)) { + $cols[] = $this->quoteNamesIn($val); + } else { + $cols[] = $this->quoteNamesIn("$val AS $key"); + } + } + + return $this->indentCsv($cols); + } + + /** + * 生成 FROM 语句. + * + * @return string + */ + protected function buildFrom() + { + if (!$this->from) { + return ''; + } + + $refs = array(); + foreach ($this->from as $from) { + $refs[] = implode(' ', $from); + } + return ' FROM' . $this->indentCsv($refs); + } + + /** + * 生成 GROUP BY 语句. + * + * @return string + */ + protected function buildGroupBy() + { + if (!$this->group_by) { + return ''; + } + return ' GROUP BY' . $this->indentCsv($this->group_by); + } + + /** + * 生成 HAVING 语句. + * + * @return string + */ + protected function buildHaving() + { + if (!$this->having) { + return ''; + } + return ' HAVING' . $this->indent($this->having); + } + + /** + * 生成 FOR UPDATE 语句 + * + * @return string + */ + protected function buildForUpdate() + { + if (!$this->for_update) { + return ''; + } + return ' FOR UPDATE'; + } + + /** + * where + * + * @param string|array $cond + * @return self + */ + public function where($cond) + { + if (is_array($cond)) { + foreach ($cond as $key => $val) { + if (is_string($key)) { + $this->addWhere('AND', array($key, $val)); + } else { + $this->addWhere('AND', array($val)); + } + } + } else { + $this->addWhere('AND', func_get_args()); + } + return $this; + } + + /** + * or where + * + * @param string|array $cond + * @return self + */ + public function orWhere($cond) + { + if (is_array($cond)) { + foreach ($cond as $key => $val) { + if (is_string($key)) { + $this->addWhere('OR', array($key, $val)); + } else { + $this->addWhere('OR', array($val)); + } + } + } else { + $this->addWhere('OR', func_get_args()); + } + return $this; + } + + /** + * limit + * + * @param int $limit + * @return self + */ + public function limit($limit) + { + $this->limit = (int)$limit; + return $this; + } + + /** + * limit offset + * + * @param int $offset + * @return self + */ + public function offset($offset) + { + $this->offset = (int)$offset; + return $this; + } + + /** + * orderby. + * + * @param array $cols + * @return self + */ + public function orderBy(array $cols) + { + return $this->addOrderBy($cols); + } + + /** + * order by ASC OR DESC + * + * @param array $cols + * @param bool $order_asc + * @return self + */ + public function orderByASC(array $cols, $order_asc = true) + { + $this->order_asc = $order_asc; + return $this->addOrderBy($cols); + } + + /** + * order by DESC + * + * @param array $cols + * @return self + */ + public function orderByDESC(array $cols) + { + $this->order_asc = false; + return $this->addOrderBy($cols); + } + + // -------------abstractquery---------- + /** + * 返回逗号分隔的字符串 + * + * @param array $list + * @return string + */ + protected function indentCsv(array $list) + { + return ' ' . implode(',', $list); + } + + /** + * 返回空格分隔的字符串 + * + * @param array $list + * @return string + */ + protected function indent(array $list) + { + return ' ' . implode(' ', $list); + } + + /** + * 批量为占位符绑定值 + * + * @param array $bind_values + * @return self + * + */ + public function bindValues(array $bind_values) + { + foreach ($bind_values as $key => $val) { + $this->bindValue($key, $val); + } + return $this; + } + + /** + * 单个为占位符绑定值 + * + * @param string $name + * @param mixed $value + * @return self + */ + public function bindValue($name, $value) + { + $this->bind_values[$name] = $value; + return $this; + } + + /** + * 生成 flag + * + * @return string + */ + protected function buildFlags() + { + if (!$this->flags) { + return ''; + } + return ' ' . implode(' ', array_keys($this->flags)); + } + + /** + * 设置 flag. + * + * @param string $flag + * @param bool $enable + */ + protected function setFlag($flag, $enable = true) + { + if ($enable) { + $this->flags[$flag] = true; + } else { + unset($this->flags[$flag]); + } + } + + /** + * 重置 flag + */ + protected function resetFlags() + { + $this->flags = array(); + } + + /** + * + * 添加 where 语句 + * + * @param string $andor 'AND' or 'OR + * @param array $conditions + * @return self + * + */ + protected function addWhere($andor, $conditions) + { + $this->addClauseCondWithBind('where', $andor, $conditions); + return $this; + } + + /** + * 添加条件和绑定值 + * + * @param string $clause where 、having等 + * @param string $andor AND、OR等 + * @param array $conditions + */ + protected function addClauseCondWithBind($clause, $andor, $conditions) + { + $cond = array_shift($conditions); + $cond = $this->quoteNamesIn($cond); + + $bind =& $this->{"bind_{$clause}"}; + foreach ($conditions as $value) { + $bind[] = $value; + } + + $clause =& $this->$clause; + if ($clause) { + $clause[] = "$andor $cond"; + } else { + $clause[] = $cond; + } + } + + /** + * 生成 where 语句 + * + * @return string + */ + protected function buildWhere() + { + if (!$this->where) { + return ''; + } + return ' WHERE' . $this->indent($this->where); + } + + /** + * 增加 order by + * + * @param array $spec The columns and direction to order by. + * @return self + */ + protected function addOrderBy(array $spec) + { + foreach ($spec as $col) { + $this->order_by[] = $this->quoteNamesIn($col); + } + return $this; + } + + /** + * 生成 order by 语句 + * + * @return string + */ + protected function buildOrderBy() + { + if (!$this->order_by) { + return ''; + } + + if ($this->order_asc) { + return ' ORDER BY' . $this->indentCsv($this->order_by) . ' ASC'; + } else { + return ' ORDER BY' . $this->indentCsv($this->order_by) . ' DESC'; + } + } + + /** + * 生成 limit 语句 + * + * @return string + */ + protected function buildLimit() + { + $has_limit = $this->type == 'DELETE' || $this->type == 'UPDATE'; + $has_offset = $this->type == 'SELECT'; + + if ($has_offset && $this->limit) { + $clause = " LIMIT {$this->limit}"; + if ($this->offset) { + $clause .= " OFFSET {$this->offset}"; + } + return $clause; + } elseif ($has_limit && $this->limit) { + return " LIMIT {$this->limit}"; + } + return ''; + } + + /** + * Quotes + * + * @param string $spec + * @return string|array + */ + public function quoteName($spec) + { + $spec = trim($spec); + $seps = array(' AS ', ' ', '.'); + foreach ($seps as $sep) { + $pos = strripos($spec, $sep); + if ($pos) { + return $this->quoteNameWithSeparator($spec, $sep, $pos); + } + } + return $this->replaceName($spec); + } + + /** + * 指定分隔符的 Quotes + * + * @param string $spec + * @param string $sep + * @param int $pos + * @return string + */ + protected function quoteNameWithSeparator($spec, $sep, $pos) + { + $len = strlen($sep); + $part1 = $this->quoteName(substr($spec, 0, $pos)); + $part2 = $this->replaceName(substr($spec, $pos + $len)); + return "{$part1}{$sep}{$part2}"; + } + + /** + * Quotes "table.col" 格式的字符串 + * + * @param string $text + * @return string|array + */ + public function quoteNamesIn($text) + { + $list = $this->getListForQuoteNamesIn($text); + $last = count($list) - 1; + $text = null; + foreach ($list as $key => $val) { + if (($key + 1) % 3) { + $text .= $this->quoteNamesInLoop($val, $key == $last); + } + } + return $text; + } + + /** + * 返回 quote 元素列表 + * + * @param string $text + * @return array + */ + protected function getListForQuoteNamesIn($text) + { + $apos = "'"; + $quot = '"'; + return preg_split( + "/(($apos+|$quot+|\\$apos+|\\$quot+).*?\\2)/", + $text, + -1, + PREG_SPLIT_DELIM_CAPTURE + ); + } + + /** + * 循环 quote + * + * @param string $val + * @param bool $is_last + * @return string + */ + protected function quoteNamesInLoop($val, $is_last) + { + if ($is_last) { + return $this->replaceNamesAndAliasIn($val); + } + return $this->replaceNamesIn($val); + } + + /** + * 替换成别名 + * + * @param string $val + * @return string + */ + protected function replaceNamesAndAliasIn($val) + { + $quoted = $this->replaceNamesIn($val); + $pos = strripos($quoted, ' AS '); + if ($pos !== false) { + $bracket = strripos($quoted, ')'); + if ($bracket === false) { + $alias = $this->replaceName(substr($quoted, $pos + 4)); + $quoted = substr($quoted, 0, $pos) . " AS $alias"; + } + } + return $quoted; + } + + /** + * Quotes name + * + * @param string $name + * @return string + */ + protected function replaceName($name) + { + $name = trim($name); + if ($name == '*') { + return $name; + } + return '`' . $name . '`'; + } + + /** + * Quotes + * + * @param string $text + * @return string|array + */ + protected function replaceNamesIn($text) + { + $is_string_literal = strpos($text, "'") !== false + || strpos($text, '"') !== false; + if ($is_string_literal) { + return $text; + } + + $word = '[a-z_][a-z0-9_]*'; + + $find = "/(\\b)($word)\\.($word)(\\b)/i"; + + $repl = '$1`$2`.`$3`$4'; + + $text = preg_replace($find, $repl, $text); + + return $text; + } + + // ---------- insert -------------- + /** + * 设置 `table.column` 与 last-insert-id 的映射 + * + * @param array $last_insert_id_names + */ + public function setLastInsertIdNames(array $last_insert_id_names) + { + $this->last_insert_id_names = $last_insert_id_names; + } + + /** + * insert into. + * + * @param string $table + * @return self + */ + public function into($table) + { + $this->table = $this->quoteName($table); + return $this; + } + + /** + * 生成 INSERT 语句 + * + * @return string + */ + protected function buildINSERT() + { + return 'INSERT' + . $this->buildFlags() + . $this->buildInto() + . $this->buildValuesForInsert() + . $this->buildReturning(); + } + + /** + * 生成 INTO 语句 + * + * @return string + */ + protected function buildInto() + { + return " INTO " . $this->table; + } + + /** + * PDO::lastInsertId() + * + * @param string $col + * @return mixed + */ + public function getLastInsertIdName($col) + { + $key = str_replace('`', '', $this->table) . '.' . $col; + if (isset($this->last_insert_id_names[$key])) { + return $this->last_insert_id_names[$key]; + } + + return null; + } + + /** + * 设置一列,如果有第二各参数,则把第二个参数绑定在占位符上 + * + * @param string $col + * @return self + */ + public function col($col) + { + return call_user_func_array(array($this, 'addCol'), func_get_args()); + } + + /** + * 设置多列 + * + * @param array $cols + * @return self + */ + public function cols(array $cols) + { + if ($this->type == 'SELECT') { + foreach ($cols as $key => $val) { + $this->addColSELECT($key, $val); + } + return $this; + } + return $this->addCols($cols); + } + + /** + * 直接设置列的值 + * + * @param string $col + * @param string $value + * @return self + */ + public function set($col, $value) + { + return $this->setCol($col, $value); + } + + /** + * 为 INSERT 语句绑定值 + * + * @return string + */ + protected function buildValuesForInsert() + { + return ' (' . $this->indentCsv(array_keys($this->col_values)) . ') VALUES (' . + $this->indentCsv(array_values($this->col_values)) . ')'; + } + + // ------update------- + /** + * 更新哪个表 + * + * @param string $table + * @return self + */ + public function table($table) + { + $this->table = $this->quoteName($table); + return $this; + } + + /** + * 生成完整 SQL 语句 + * + * @return string + * @throws Exception + */ + protected function build() + { + switch ($this->type) { + case 'DELETE': + return $this->buildDELETE(); + case 'INSERT': + return $this->buildINSERT(); + case 'UPDATE': + return $this->buildUPDATE(); + case 'SELECT': + return $this->buildSELECT(); + } + throw new Exception("type empty"); + } + + /** + * 生成更新的 SQL 语句 + */ + protected function buildUPDATE() + { + return 'UPDATE' + . $this->buildFlags() + . $this->buildTable() + . $this->buildValuesForUpdate() + . $this->buildWhere() + . $this->buildOrderBy() + . $this->buildLimit() + . $this->buildReturning(); + } + + /** + * 哪个表 + * + * @return string + */ + protected function buildTable() + { + return " {$this->table}"; + } + + /** + * 为更新语句绑定值 + * + * @return string + */ + protected function buildValuesForUpdate() + { + $values = array(); + foreach ($this->col_values as $col => $value) { + $values[] = "{$col} = {$value}"; + } + return ' SET' . $this->indentCsv($values); + } + + // ----------Dml--------------- + /** + * 获取绑定的值 + * + * @return array + */ + public function getBindValuesCOMMON() + { + $bind_values = $this->bind_values; + $i = 1; + foreach ($this->bind_where as $val) { + $bind_values[$i] = $val; + $i++; + } + return $bind_values; + } + + /** + * 设置列 + * + * @param string $col + * @return self + */ + protected function addCol($col) + { + $key = $this->quoteName($col); + $this->col_values[$key] = ":$col"; + $args = func_get_args(); + if (count($args) > 1) { + $this->bindValue($col, $args[1]); + } + return $this; + } + + /** + * 设置多个列 + * + * @param array $cols + * @return self + */ + protected function addCols(array $cols) + { + foreach ($cols as $key => $val) { + if (is_int($key)) { + $this->addCol($val); + } else { + $this->addCol($key, $val); + } + } + return $this; + } + + /** + * 设置单列的值 + * + * @param string $col . + * @param string $value + * @return self + */ + protected function setCol($col, $value) + { + if ($value === null) { + $value = 'NULL'; + } + + $key = $this->quoteName($col); + $value = $this->quoteNamesIn($value); + $this->col_values[$key] = $value; + return $this; + } + + /** + * 增加返回的列 + * + * @param array $cols + * @return self + * + */ + protected function addReturning(array $cols) + { + foreach ($cols as $col) { + $this->returning[] = $this->quoteNamesIn($col); + } + return $this; + } + + /** + * 生成 RETURNING 语句 + * + * @return string + */ + protected function buildReturning() + { + if (!$this->returning) { + return ''; + } + return ' RETURNING' . $this->indentCsv($this->returning); + } + + /** + * 构造函数 + * + * @param string $host + * @param int $port + * @param string $user + * @param string $password + * @param string $db_name + * @param string $charset + */ + public function __construct($host, $port, $user, $password, $db_name, $charset = 'utf8') + { + $this->settings = array( + 'host' => $host, + 'port' => $port, + 'user' => $user, + 'password' => $password, + 'dbname' => $db_name, + 'charset' => $charset, + ); + $this->connect(); + } + + /** + * 创建 PDO 实例 + */ + protected function connect() + { + $dsn = 'mysql:dbname=' . $this->settings["dbname"] . ';host=' . + $this->settings["host"] . ';port=' . $this->settings['port']; + $this->pdo = new PDO($dsn, $this->settings["user"], $this->settings["password"], + array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . (!empty($this->settings['charset']) ? + $this->settings['charset'] : 'utf8') + )); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + } + + /** + * 关闭连接 + */ + public function closeConnection() + { + $this->pdo = null; + } + + /** + * 执行 + * + * @param string $query + * @param string $parameters + * @throws PDOException + */ + protected function execute($query, $parameters = "") + { + try { + if (is_null($this->pdo)) { + $this->connect(); + } + $this->sQuery = @$this->pdo->prepare($query); + $this->bindMore($parameters); + if (!empty($this->parameters)) { + foreach ($this->parameters as $param) { + $this->sQuery->bindParam($param[0], $param[1]); + } + } + $this->success = $this->sQuery->execute(); + } catch (PDOException $e) { + // 服务端断开时重连一次 + if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) { + $this->closeConnection(); + $this->connect(); + + try { + $this->sQuery = $this->pdo->prepare($query); + $this->bindMore($parameters); + if (!empty($this->parameters)) { + foreach ($this->parameters as $param) { + $this->sQuery->bindParam($param[0], $param[1]); + } + } + $this->success = $this->sQuery->execute(); + } catch (PDOException $ex) { + $this->rollBackTrans(); + throw $ex; + } + } else { + $this->rollBackTrans(); + $msg = $e->getMessage(); + $err_msg = "SQL:".$this->lastSQL()." ".$msg; + $exception = new \PDOException($err_msg, (int)$e->getCode()); + throw $exception; + } + } + $this->parameters = array(); + } + + /** + * 绑定 + * + * @param string $para + * @param string $value + */ + public function bind($para, $value) + { + if (is_string($para)) { + $this->parameters[sizeof($this->parameters)] = array(":" . $para, $value); + } else { + $this->parameters[sizeof($this->parameters)] = array($para, $value); + } + } + + /** + * 绑定多个 + * + * @param array $parray + */ + public function bindMore($parray) + { + if (empty($this->parameters) && is_array($parray)) { + $columns = array_keys($parray); + foreach ($columns as $i => &$column) { + $this->bind($column, $parray[$column]); + } + } + } + + /** + * 执行 SQL + * + * @param string $query + * @param array $params + * @param int $fetchmode + * @return mixed + */ + public function query($query = '', $params = null, $fetchmode = PDO::FETCH_ASSOC) + { + $query = trim($query); + if (empty($query)) { + $query = $this->build(); + if (!$params) { + $params = $this->getBindValues(); + } + } + + $this->resetAll(); + $this->lastSql = $query; + + $this->execute($query, $params); + + $rawStatement = explode(" ", $query); + + $statement = strtolower(trim($rawStatement[0])); + if ($statement === 'select' || $statement === 'show') { + return $this->sQuery->fetchAll($fetchmode); + } elseif ($statement === 'update' || $statement === 'delete' || $statement === 'replace') { + return $this->sQuery->rowCount(); + } elseif ($statement === 'insert') { + if ($this->sQuery->rowCount() > 0) { + return $this->lastInsertId(); + } + } else { + return null; + } + + return null; + } + + /** + * 返回一列 + * + * @param string $query + * @param array $params + * @return array + */ + public function column($query = '', $params = null) + { + $query = trim($query); + if (empty($query)) { + $query = $this->build(); + if (!$params) { + $params = $this->getBindValues(); + } + } + + $this->resetAll(); + $this->lastSql = $query; + + $this->execute($query, $params); + $columns = $this->sQuery->fetchAll(PDO::FETCH_NUM); + $column = null; + foreach ($columns as $cells) { + $column[] = $cells[0]; + } + return $column; + } + + /** + * 返回一行 + * + * @param string $query + * @param array $params + * @param int $fetchmode + * @return array + */ + public function row($query = '', $params = null, $fetchmode = PDO::FETCH_ASSOC) + { + $query = trim($query); + if (empty($query)) { + $query = $this->build(); + if (!$params) { + $params = $this->getBindValues(); + } + } + + $this->resetAll(); + $this->lastSql = $query; + + $this->execute($query, $params); + return $this->sQuery->fetch($fetchmode); + } + + /** + * 返回单个值 + * + * @param string $query + * @param array $params + * @return string + */ + public function single($query = '', $params = null) + { + $query = trim($query); + if (empty($query)) { + $query = $this->build(); + if (!$params) { + $params = $this->getBindValues(); + } + } + + $this->resetAll(); + $this->lastSql = $query; + + $this->execute($query, $params); + return $this->sQuery->fetchColumn(); + } + + /** + * 返回 lastInsertId + * + * @return string + */ + public function lastInsertId() + { + return $this->pdo->lastInsertId(); + } + + /** + * 返回最后一条执行的 sql + * + * @return string + */ + public function lastSQL() + { + return $this->lastSql; + } + + /** + * 开始事务 + */ + public function beginTrans() + { + try { + if (is_null($this->pdo)) { + $this->connect(); + } + return $this->pdo->beginTransaction(); + } catch (PDOException $e) { + // 服务端断开时重连一次 + if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) { + $this->closeConnection(); + $this->connect(); + return $this->pdo->beginTransaction(); + } else { + throw $e; + } + } + } + + /** + * 提交事务 + */ + public function commitTrans() + { + return $this->pdo->commit(); + } + + /** + * 事务回滚 + */ + public function rollBackTrans() + { + if ($this->pdo->inTransaction()) { + return $this->pdo->rollBack(); + } + return true; + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman.log b/addons/weliam_smartcity/vendor/workerman/workerman.log new file mode 100644 index 0000000..3aab14a --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman.log @@ -0,0 +1,649 @@ +2021-03-05 14:58:33 pid:1 Error: Class 'Im' not found in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php:32 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #18) +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php(36): Workerman\Worker::runAll() +#7 {main} +2021-03-05 14:58:33 pid:1 Worker process terminated +2021-03-05 14:59:04 pid:1 Error: Class 'Im' not found in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php:32 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #18) +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php(36): Workerman\Worker::runAll() +#7 {main} +2021-03-05 14:59:04 pid:1 Worker process terminated +2021-03-05 15:32:32 pid:1 Error: Class 'Im' not found in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php:32 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #18) +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php(36): Workerman\Worker::runAll() +#7 {main} +2021-03-05 15:32:32 pid:1 Worker process terminated +2021-03-05 15:32:55 pid:1 Error: Class 'Im' not found in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php:32 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #18) +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php(36): Workerman\Worker::runAll() +#7 {main} +2021-03-05 15:32:55 pid:1 Worker process terminated +2021-03-05 15:34:26 pid:1 Error: Class 'ImModuleUniapp' not found in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php:32 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #18) +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\api\Im.php(36): Workerman\Worker::runAll() +#7 {main} +2021-03-05 15:34:26 pid:1 Worker process terminated +2021-03-05 17:20:53 pid:1 ArgumentCountError: Too few arguments to function {closure}(), 1 passed in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php on line 931 and exactly 2 expected in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:58 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(931): {closure}(Object(Workerman\Connection\TcpConnection)) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(755): Workerman\Connection\TcpConnection->destroy() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(561): Workerman\Connection\TcpConnection->doSslHandshake(Resource id #64) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #64) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(168): Workerman\Worker::runAll() +#9 {main} +2021-03-05 17:20:53 pid:1 Worker process terminated +2021-03-09 16:34:34 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(40): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #67) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(204): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:34:34 pid:1 Worker process terminated +2021-03-09 16:34:52 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(40): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #67) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(204): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:34:52 pid:1 Worker process terminated +2021-03-09 16:35:46 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:141 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(40): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #67) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(206): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:35:46 pid:1 Worker process terminated +2021-03-09 16:37:03 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:141 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(40): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #67) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(206): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:37:03 pid:1 Worker process terminated +2021-03-09 16:37:46 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:142 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(41): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #68) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(207): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:37:46 pid:1 Worker process terminated +2021-03-09 16:38:01 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:143 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(41): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #68) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(208): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:38:01 pid:1 Worker process terminated +2021-03-09 16:53:16 pid:1 PDOException: SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO) in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1711 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1711): PDO->__construct('mysql:dbname=;h...', NULL, NULL, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1698): Workerman\MySQL\Connection->connect() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(41): Workerman\MySQL\Connection->__construct(NULL, NULL, NULL, NULL, NULL) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2415): {closure}(Object(Workerman\Worker)) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(171): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:53:17 pid:1 Worker process terminated +2021-03-09 16:54:22 pid:1 PDOException: SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO) in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1711 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1711): PDO->__construct('mysql:dbname=;h...', NULL, NULL, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1698): Workerman\MySQL\Connection->connect() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(41): Workerman\MySQL\Connection->__construct(NULL, NULL, NULL, NULL, NULL) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2415): {closure}(Object(Workerman\Worker)) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(171): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:54:23 pid:1 Worker process terminated +2021-03-09 16:55:24 pid:1 PDOException: SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO) in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1711 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1711): PDO->__construct('mysql:dbname=;h...', NULL, NULL, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1698): Workerman\MySQL\Connection->connect() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(42): Workerman\MySQL\Connection->__construct(NULL, NULL, NULL, NULL, NULL) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2415): {closure}(Object(Workerman\Worker)) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(172): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:55:25 pid:1 Worker process terminated +2021-03-09 16:55:35 pid:1 PDOException: SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO) in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1711 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1711): PDO->__construct('mysql:dbname=;h...', NULL, NULL, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1698): Workerman\MySQL\Connection->connect() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(42): Workerman\MySQL\Connection->__construct(NULL, NULL, NULL, NULL, NULL) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2415): {closure}(Object(Workerman\Worker)) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(172): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:55:36 pid:1 Worker process terminated +2021-03-09 16:55:52 pid:1 PDOException: SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO) in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1711 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1711): PDO->__construct('mysql:dbname=;h...', NULL, NULL, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1698): Workerman\MySQL\Connection->connect() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(42): Workerman\MySQL\Connection->__construct(NULL, NULL, NULL, NULL, NULL) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2415): {closure}(Object(Workerman\Worker)) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(172): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:55:53 pid:1 Worker process terminated +2021-03-09 16:59:18 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:143 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(219): Workerman\Worker::runAll() +#8 {main} +2021-03-09 16:59:18 pid:1 Worker process terminated +2021-03-09 17:05:09 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(205): Workerman\Worker::runAll() +#8 {main} +2021-03-09 17:05:09 pid:1 Worker process terminated +2021-03-09 17:05:13 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(205): Workerman\Worker::runAll() +#8 {main} +2021-03-09 17:05:13 pid:1 Worker process terminated +2021-03-09 17:05:18 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(205): Workerman\Worker::runAll() +#8 {main} +2021-03-09 17:05:18 pid:1 Worker process terminated +2021-03-09 17:05:30 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(205): Workerman\Worker::runAll() +#8 {main} +2021-03-09 17:05:30 pid:1 Worker process terminated +2021-03-09 17:06:02 pid:1 Error: Access to undeclared static property: handle::$table in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:139 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(206): Workerman\Worker::runAll() +#8 {main} +2021-03-09 17:06:02 pid:1 Worker process terminated +2021-03-09 17:20:39 pid:1 PDOException: SQL:INSERT INTO ``ims_wlmerchant_im`` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ims_wlmerchant_im`` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type' at line 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO ``i...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(222): Workerman\Worker::runAll() +#10 {main} +2021-03-09 17:20:39 pid:1 Worker process terminated +2021-03-09 17:21:21 pid:1 PDOException: SQL:INSERT INTO ``ims_wlmerchant_im`` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ims_wlmerchant_im`` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type' at line 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO ``i...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(101): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(223): Workerman\Worker::runAll() +#10 {main} +2021-03-09 17:21:21 pid:1 Worker process terminated +2021-03-12 10:02:19 pid:1 Error: Call to a member function send() on int in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:203 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(178): handle->socketSend(3, '\xE9\x80\x9A\xE8\xAE\xAF\xE8\xAE\xB0\xE5\xBD\x95', 4, 5, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(222): Workerman\Worker::runAll() +#9 {main} +2021-03-12 10:02:19 pid:1 Worker process terminated +2021-03-12 10:02:52 pid:1 Error: Call to a member function send() on int in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:203 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(178): handle->socketSend(24, '\xE9\x80\x9A\xE8\xAE\xAF\xE8\xAE\xB0\xE5\xBD\x95', 4, 5, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(222): Workerman\Worker::runAll() +#9 {main} +2021-03-12 10:02:52 pid:1 Worker process terminated +2021-03-12 11:16:54 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:200 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(173): handle->socketSend(3, '\xE9\x80\x9A\xE8\xAE\xAF\xE8\xAE\xB0\xE5\xBD\x95', 4, 5, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(292): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:16:54 pid:1 Worker process terminated +2021-03-12 11:17:20 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:200 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(173): handle->socketSend(3, '\xE9\x80\x9A\xE8\xAE\xAF\xE8\xAE\xB0\xE5\xBD\x95', 4, 5, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(292): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:17:20 pid:1 Worker process terminated +2021-03-12 11:18:23 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:200 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(173): handle->socketSend(3, '\xE9\x80\x9A\xE8\xAE\xAF\xE8\xAE\xB0\xE5\xBD\x95', 4, 5, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":4,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(292): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:18:23 pid:1 Worker process terminated +2021-03-12 11:18:57 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:200 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(103): handle->socketSend(24, '\xE8\xBF\x99\xE9\x87\x8C\xE6\x98\xAF\xE5\x8F\x91\xE9\x80\x81...', 2, 0) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(296): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:18:57 pid:1 Worker process terminated +2021-03-12 11:19:18 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:200 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(103): handle->socketSend(24, '\xE8\xBF\x99\xE9\x87\x8C\xE6\x98\xAF\xE5\x8F\x91\xE9\x80\x81...', 2, 0) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(296): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:19:18 pid:1 Worker process terminated +2021-03-12 11:20:17 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:204 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(103): handle->socketSend(24, '\xE8\xBF\x99\xE9\x87\x8C\xE6\x98\xAF\xE5\x8F\x91\xE9\x80\x81...', 2, 0) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(249): Workerman\Worker::runAll() +#9 {main} +2021-03-12 11:20:17 pid:1 Worker process terminated +2021-03-18 11:02:30 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:218 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(119): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(265): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:02:30 pid:1 Worker process terminated +2021-03-18 11:03:16 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:218 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(119): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(265): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:03:16 pid:1 Worker process terminated +2021-03-18 11:05:22 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:219 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(120): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(266): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:05:22 pid:1 Worker process terminated +2021-03-18 11:05:54 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:217 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(118): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(264): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:05:54 pid:1 Worker process terminated +2021-03-18 11:06:19 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:217 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(118): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(264): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:06:19 pid:1 Worker process terminated +2021-03-18 11:06:37 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:216 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(118): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(263): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:06:37 pid:1 Worker process terminated +2021-03-18 11:07:08 pid:1 Error: Call to a member function send() on null in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php:216 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(118): handle->socketSend(24, '\xE5\x8D\xB3\xE6\x97\xB6\xE9\x80\x9A\xE8\xAE\xAF\xE4\xBF\xA1...', 2, 0, Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(263): Workerman\Worker::runAll() +#9 {main} +2021-03-18 11:07:08 pid:1 Worker process terminated +2021-03-18 11:14:16 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x8A\xF0\x9F...' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(280): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:14:16 pid:1 Worker process terminated +2021-03-18 11:16:05 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\xA2\xF0\x9F...' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(280): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:16:05 pid:1 Worker process terminated +2021-03-18 11:17:07 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\xA4\xA6\xE2\x80...' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(280): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:17:07 pid:1 Worker process terminated +2021-03-18 11:17:36 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x8E\xB6' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(101): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(281): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:17:36 pid:1 Worker process terminated +2021-03-18 11:18:25 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x81' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(101): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(281): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:18:25 pid:1 Worker process terminated +2021-03-18 11:18:46 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x8E\xB6' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(281): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:18:46 pid:1 Worker process terminated +2021-03-18 11:19:21 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\xA4\xA6\xE2\x80...' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(281): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:19:21 pid:1 Worker process terminated +2021-03-18 11:19:46 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x8D' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(282): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:19:46 pid:1 Worker process terminated +2021-03-18 11:20:11 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x90\xB1\xE2\x80...' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(100): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(283): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:20:11 pid:1 Worker process terminated +2021-03-18 11:20:34 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x81' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(101): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(284): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:20:34 pid:1 Worker process terminated +2021-03-18 11:21:27 pid:1 PDOException: SQL:INSERT INTO `ims_wlmerchant_im` ( `uniacid`,`send_id`,`send_type`,`receive_id`,`receive_type`,`create_time`,`type`,`content`) VALUES ( :uniacid,:send_id,:send_type,:receive_id,:receive_type,:create_time,:type,:content) SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x8D' for column 'content' at row 1 in D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php:1770 +Stack trace: +#0 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\mysql\src\Connection.php(1828): Workerman\MySQL\Connection->execute('INSERT INTO `im...', Array) +#1 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(101): Workerman\MySQL\Connection->query() +#2 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(47): handle->handleData() +#3 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Connection\TcpConnection.php(637): {closure}(Object(Workerman\Connection\TcpConnection), '{"im_type":2,"s...') +#4 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Events\Select.php(293): Workerman\Connection\TcpConnection->baseRead(Resource id #70) +#5 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(2430): Workerman\Events\Select->loop() +#6 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1418): Workerman\Worker->run() +#7 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(1361): Workerman\Worker::forkWorkersForWindows() +#8 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\vendor\workerman\workerman\Worker.php(542): Workerman\Worker::forkWorkers() +#9 D:\Software\phpstudy_pro\WWW\merchant\addons\weliam_smartcity\core\common\Im.php(281): Workerman\Worker::runAll() +#10 {main} +2021-03-18 11:21:27 pid:1 Worker process terminated diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/.gitignore b/addons/weliam_smartcity/vendor/workerman/workerman/.gitignore new file mode 100644 index 0000000..f3f9e18 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/.gitignore @@ -0,0 +1,6 @@ +logs +.buildpath +.project +.settings +.idea +.DS_Store diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Autoloader.php b/addons/weliam_smartcity/vendor/workerman/workerman/Autoloader.php new file mode 100644 index 0000000..7d760e9 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Autoloader.php @@ -0,0 +1,69 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman; + +/** + * Autoload. + */ +class Autoloader +{ + /** + * Autoload root path. + * + * @var string + */ + protected static $_autoloadRootPath = ''; + + /** + * Set autoload root path. + * + * @param string $root_path + * @return void + */ + public static function setRootPath($root_path) + { + self::$_autoloadRootPath = $root_path; + } + + /** + * Load files by namespace. + * + * @param string $name + * @return boolean + */ + public static function loadByNamespace($name) + { + $class_path = \str_replace('\\', \DIRECTORY_SEPARATOR, $name); + if (\strpos($name, 'Workerman\\') === 0) { + $class_file = __DIR__ . \substr($class_path, \strlen('Workerman')) . '.php'; + } else { + if (self::$_autoloadRootPath) { + $class_file = self::$_autoloadRootPath . \DIRECTORY_SEPARATOR . $class_path . '.php'; + } + if (empty($class_file) || !\is_file($class_file)) { + $class_file = __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . "$class_path.php"; + } + } + + if (\is_file($class_file)) { + require_once($class_file); + if (\class_exists($name, false)) { + return true; + } + } + return false; + } +} + +\spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncTcpConnection.php b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncTcpConnection.php new file mode 100644 index 0000000..6d0c3b5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncTcpConnection.php @@ -0,0 +1,377 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +use Workerman\Events\EventInterface; +use Workerman\Lib\Timer; +use Workerman\Worker; +use \Exception; + +/** + * AsyncTcpConnection. + */ +class AsyncTcpConnection extends TcpConnection +{ + /** + * Emitted when socket connection is successfully established. + * + * @var callable|null + */ + public $onConnect = null; + + /** + * Transport layer protocol. + * + * @var string + */ + public $transport = 'tcp'; + + /** + * Status. + * + * @var int + */ + protected $_status = self::STATUS_INITIAL; + + /** + * Remote host. + * + * @var string + */ + protected $_remoteHost = ''; + + /** + * Remote port. + * + * @var int + */ + protected $_remotePort = 80; + + /** + * Connect start time. + * + * @var float + */ + protected $_connectStartTime = 0; + + /** + * Remote URI. + * + * @var string + */ + protected $_remoteURI = ''; + + /** + * Context option. + * + * @var array + */ + protected $_contextOption = null; + + /** + * Reconnect timer. + * + * @var int + */ + protected $_reconnectTimer = null; + + + /** + * PHP built-in protocols. + * + * @var array + */ + protected static $_builtinTransports = array( + 'tcp' => 'tcp', + 'udp' => 'udp', + 'unix' => 'unix', + 'ssl' => 'ssl', + 'sslv2' => 'sslv2', + 'sslv3' => 'sslv3', + 'tls' => 'tls' + ); + + /** + * Construct. + * + * @param string $remote_address + * @param array $context_option + * @throws Exception + */ + public function __construct($remote_address, array $context_option = array()) + { + $address_info = \parse_url($remote_address); + if (!$address_info) { + list($scheme, $this->_remoteAddress) = \explode(':', $remote_address, 2); + if (!$this->_remoteAddress) { + Worker::safeEcho(new \Exception('bad remote_address')); + } + } else { + if (!isset($address_info['port'])) { + $address_info['port'] = 0; + } + if (!isset($address_info['path'])) { + $address_info['path'] = '/'; + } + if (!isset($address_info['query'])) { + $address_info['query'] = ''; + } else { + $address_info['query'] = '?' . $address_info['query']; + } + $this->_remoteAddress = "{$address_info['host']}:{$address_info['port']}"; + $this->_remoteHost = $address_info['host']; + $this->_remotePort = $address_info['port']; + $this->_remoteURI = "{$address_info['path']}{$address_info['query']}"; + $scheme = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp'; + } + + $this->id = $this->_id = self::$_idRecorder++; + if(\PHP_INT_MAX === self::$_idRecorder){ + self::$_idRecorder = 0; + } + // Check application layer protocol class. + if (!isset(self::$_builtinTransports[$scheme])) { + $scheme = \ucfirst($scheme); + $this->protocol = '\\Protocols\\' . $scheme; + if (!\class_exists($this->protocol)) { + $this->protocol = "\\Workerman\\Protocols\\$scheme"; + if (!\class_exists($this->protocol)) { + throw new Exception("class \\Protocols\\$scheme not exist"); + } + } + } else { + $this->transport = self::$_builtinTransports[$scheme]; + } + + // For statistics. + ++self::$statistics['connection_count']; + $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; + $this->maxPackageSize = self::$defaultMaxPackageSize; + $this->_contextOption = $context_option; + static::$connections[$this->_id] = $this; + } + + /** + * Do connect. + * + * @return void + */ + public function connect() + { + if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING && + $this->_status !== self::STATUS_CLOSED) { + return; + } + $this->_status = self::STATUS_CONNECTING; + $this->_connectStartTime = \microtime(true); + if ($this->transport !== 'unix') { + if (!$this->_remotePort) { + $this->_remotePort = $this->transport === 'ssl' ? 443 : 80; + $this->_remoteAddress = $this->_remoteHost.':'.$this->_remotePort; + } + // Open socket connection asynchronously. + if ($this->_contextOption) { + $context = \stream_context_create($this->_contextOption); + $this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}", + $errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT, $context); + } else { + $this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}", + $errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT); + } + } else { + $this->_socket = \stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, + \STREAM_CLIENT_ASYNC_CONNECT); + } + // If failed attempt to emit onError callback. + if (!$this->_socket || !\is_resource($this->_socket)) { + $this->emitError(\WORKERMAN_CONNECT_FAIL, $errstr); + if ($this->_status === self::STATUS_CLOSING) { + $this->destroy(); + } + if ($this->_status === self::STATUS_CLOSED) { + $this->onConnect = null; + } + return; + } + // Add socket to global event loop waiting connection is successfully established or faild. + Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection')); + // For windows. + if(\DIRECTORY_SEPARATOR === '\\') { + Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection')); + } + } + + /** + * Reconnect. + * + * @param int $after + * @return void + */ + public function reconnect($after = 0) + { + $this->_status = self::STATUS_INITIAL; + static::$connections[$this->_id] = $this; + if ($this->_reconnectTimer) { + Timer::del($this->_reconnectTimer); + } + if ($after > 0) { + $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false); + return; + } + $this->connect(); + } + + /** + * CancelReconnect. + */ + public function cancelReconnect() + { + if ($this->_reconnectTimer) { + Timer::del($this->_reconnectTimer); + } + } + + /** + * Get remote address. + * + * @return string + */ + public function getRemoteHost() + { + return $this->_remoteHost; + } + + /** + * Get remote URI. + * + * @return string + */ + public function getRemoteURI() + { + return $this->_remoteURI; + } + + /** + * Try to emit onError callback. + * + * @param int $code + * @param string $msg + * @return void + */ + protected function emitError($code, $msg) + { + $this->_status = self::STATUS_CLOSING; + if ($this->onError) { + try { + \call_user_func($this->onError, $this, $code, $msg); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } + + /** + * Check connection is successfully established or faild. + * + * @param resource $socket + * @return void + */ + public function checkConnection() + { + // Remove EV_EXPECT for windows. + if(\DIRECTORY_SEPARATOR === '\\') { + Worker::$globalEvent->del($this->_socket, EventInterface::EV_EXCEPT); + } + + // Remove write listener. + Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); + + if ($this->_status !== self::STATUS_CONNECTING) { + return; + } + + // Check socket state. + if ($address = \stream_socket_get_name($this->_socket, true)) { + // Nonblocking. + \stream_set_blocking($this->_socket, false); + // Compatible with hhvm + if (\function_exists('stream_set_read_buffer')) { + \stream_set_read_buffer($this->_socket, 0); + } + // Try to open keepalive for tcp and disable Nagle algorithm. + if (\function_exists('socket_import_stream') && $this->transport === 'tcp') { + $raw_socket = \socket_import_stream($this->_socket); + \socket_set_option($raw_socket, \SOL_SOCKET, \SO_KEEPALIVE, 1); + \socket_set_option($raw_socket, \SOL_TCP, \TCP_NODELAY, 1); + } + + // SSL handshake. + if ($this->transport === 'ssl') { + $this->_sslHandshakeCompleted = $this->doSslHandshake($this->_socket); + if ($this->_sslHandshakeCompleted === false) { + return; + } + } else { + // There are some data waiting to send. + if ($this->_sendBuffer) { + Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); + } + } + + // Register a listener waiting read event. + Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); + + $this->_status = self::STATUS_ESTABLISHED; + $this->_remoteAddress = $address; + + // Try to emit onConnect callback. + if ($this->onConnect) { + try { + \call_user_func($this->onConnect, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + // Try to emit protocol::onConnect + if ($this->protocol && \method_exists($this->protocol, 'onConnect')) { + try { + \call_user_func(array($this->protocol, 'onConnect'), $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } else { + // Connection failed. + $this->emitError(\WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(\microtime(true) - $this->_connectStartTime, 4) . ' seconds'); + if ($this->_status === self::STATUS_CLOSING) { + $this->destroy(); + } + if ($this->_status === self::STATUS_CLOSED) { + $this->onConnect = null; + } + } + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncUdpConnection.php b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncUdpConnection.php new file mode 100644 index 0000000..7df93ad --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/AsyncUdpConnection.php @@ -0,0 +1,209 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +use Workerman\Events\EventInterface; +use Workerman\Worker; +use \Exception; + +/** + * AsyncTcpConnection. + */ +class AsyncUdpConnection extends UdpConnection +{ + /** + * Emitted when socket connection is successfully established. + * + * @var callable + */ + public $onConnect = null; + + /** + * Emitted when socket connection closed. + * + * @var callable + */ + public $onClose = null; + + /** + * Connected or not. + * + * @var bool + */ + protected $connected = false; + + /** + * Context option. + * + * @var array + */ + protected $_contextOption = null; + + /** + * Construct. + * + * @param string $remote_address + * @throws Exception + */ + public function __construct($remote_address, $context_option = null) + { + // Get the application layer communication protocol and listening address. + list($scheme, $address) = \explode(':', $remote_address, 2); + // Check application layer protocol class. + if ($scheme !== 'udp') { + $scheme = \ucfirst($scheme); + $this->protocol = '\\Protocols\\' . $scheme; + if (!\class_exists($this->protocol)) { + $this->protocol = "\\Workerman\\Protocols\\$scheme"; + if (!\class_exists($this->protocol)) { + throw new Exception("class \\Protocols\\$scheme not exist"); + } + } + } + + $this->_remoteAddress = \substr($address, 2); + $this->_contextOption = $context_option; + } + + /** + * For udp package. + * + * @param resource $socket + * @return bool + */ + public function baseRead($socket) + { + $recv_buffer = \stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address); + if (false === $recv_buffer || empty($remote_address)) { + return false; + } + + if ($this->onMessage) { + if ($this->protocol) { + $parser = $this->protocol; + $recv_buffer = $parser::decode($recv_buffer, $this); + } + ++ConnectionInterface::$statistics['total_request']; + try { + \call_user_func($this->onMessage, $this, $recv_buffer); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return true; + } + + /** + * Sends data on the connection. + * + * @param string $send_buffer + * @param bool $raw + * @return void|boolean + */ + public function send($send_buffer, $raw = false) + { + if (false === $raw && $this->protocol) { + $parser = $this->protocol; + $send_buffer = $parser::encode($send_buffer, $this); + if ($send_buffer === '') { + return; + } + } + if ($this->connected === false) { + $this->connect(); + } + return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0); + } + + + /** + * Close connection. + * + * @param mixed $data + * @param bool $raw + * + * @return bool + */ + public function close($data = null, $raw = false) + { + if ($data !== null) { + $this->send($data, $raw); + } + Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); + \fclose($this->_socket); + $this->connected = false; + // Try to emit onClose callback. + if ($this->onClose) { + try { + \call_user_func($this->onClose, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + $this->onConnect = $this->onMessage = $this->onClose = null; + return true; + } + + /** + * Connect. + * + * @return void + */ + public function connect() + { + if ($this->connected === true) { + return; + } + if ($this->_contextOption) { + $context = \stream_context_create($this->_contextOption); + $this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg, + 30, \STREAM_CLIENT_CONNECT, $context); + } else { + $this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg); + } + + if (!$this->_socket) { + Worker::safeEcho(new \Exception($errmsg)); + return; + } + + \stream_set_blocking($this->_socket, false); + + if ($this->onMessage) { + Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); + } + $this->connected = true; + // Try to emit onConnect callback. + if ($this->onConnect) { + try { + \call_user_func($this->onConnect, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } + +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Connection/ConnectionInterface.php b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/ConnectionInterface.php new file mode 100644 index 0000000..05954e2 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/ConnectionInterface.php @@ -0,0 +1,125 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +/** + * ConnectionInterface. + */ +abstract class ConnectionInterface +{ + /** + * Statistics for status command. + * + * @var array + */ + public static $statistics = array( + 'connection_count' => 0, + 'total_request' => 0, + 'throw_exception' => 0, + 'send_fail' => 0, + ); + + /** + * Emitted when data is received. + * + * @var callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Sends data on the connection. + * + * @param mixed $send_buffer + * @return void|boolean + */ + abstract public function send($send_buffer); + + /** + * Get remote IP. + * + * @return string + */ + abstract public function getRemoteIp(); + + /** + * Get remote port. + * + * @return int + */ + abstract public function getRemotePort(); + + /** + * Get remote address. + * + * @return string + */ + abstract public function getRemoteAddress(); + + /** + * Get local IP. + * + * @return string + */ + abstract public function getLocalIp(); + + /** + * Get local port. + * + * @return int + */ + abstract public function getLocalPort(); + + /** + * Get local address. + * + * @return string + */ + abstract public function getLocalAddress(); + + /** + * Is ipv4. + * + * @return bool + */ + abstract public function isIPv4(); + + /** + * Is ipv6. + * + * @return bool + */ + abstract public function isIPv6(); + + /** + * Close connection. + * + * @param $data + * @return void + */ + abstract public function close($data = null); +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Connection/TcpConnection.php b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/TcpConnection.php new file mode 100644 index 0000000..28d8a02 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/TcpConnection.php @@ -0,0 +1,989 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +use Workerman\Events\EventInterface; +use Workerman\Worker; +use \Exception; + +/** + * TcpConnection. + */ +class TcpConnection extends ConnectionInterface +{ + /** + * Read buffer size. + * + * @var int + */ + const READ_BUFFER_SIZE = 65535; + + /** + * Status initial. + * + * @var int + */ + const STATUS_INITIAL = 0; + + /** + * Status connecting. + * + * @var int + */ + const STATUS_CONNECTING = 1; + + /** + * Status connection established. + * + * @var int + */ + const STATUS_ESTABLISHED = 2; + + /** + * Status closing. + * + * @var int + */ + const STATUS_CLOSING = 4; + + /** + * Status closed. + * + * @var int + */ + const STATUS_CLOSED = 8; + + /** + * Emitted when data is received. + * + * @var callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Emitted when the send buffer becomes full. + * + * @var callable + */ + public $onBufferFull = null; + + /** + * Emitted when the send buffer becomes empty. + * + * @var callable + */ + public $onBufferDrain = null; + + /** + * Application layer protocol. + * The format is like this Workerman\\Protocols\\Http. + * + * @var \Workerman\Protocols\ProtocolInterface + */ + public $protocol = null; + + /** + * Transport (tcp/udp/unix/ssl). + * + * @var string + */ + public $transport = 'tcp'; + + /** + * Which worker belong to. + * + * @var Worker + */ + public $worker = null; + + /** + * Bytes read. + * + * @var int + */ + public $bytesRead = 0; + + /** + * Bytes written. + * + * @var int + */ + public $bytesWritten = 0; + + /** + * Connection->id. + * + * @var int + */ + public $id = 0; + + /** + * A copy of $worker->id which used to clean up the connection in worker->connections + * + * @var int + */ + protected $_id = 0; + + /** + * Sets the maximum send buffer size for the current connection. + * OnBufferFull callback will be emited When the send buffer is full. + * + * @var int + */ + public $maxSendBufferSize = 1048576; + + /** + * Default send buffer size. + * + * @var int + */ + public static $defaultMaxSendBufferSize = 1048576; + + /** + * Sets the maximum acceptable packet size for the current connection. + * + * @var int + */ + public $maxPackageSize = 1048576; + + /** + * Default maximum acceptable packet size. + * + * @var int + */ + public static $defaultMaxPackageSize = 10485760; + + /** + * Id recorder. + * + * @var int + */ + protected static $_idRecorder = 1; + + /** + * Socket + * + * @var resource + */ + protected $_socket = null; + + /** + * Send buffer. + * + * @var string + */ + protected $_sendBuffer = ''; + + /** + * Receive buffer. + * + * @var string + */ + protected $_recvBuffer = ''; + + /** + * Current package length. + * + * @var int + */ + protected $_currentPackageLength = 0; + + /** + * Connection status. + * + * @var int + */ + protected $_status = self::STATUS_ESTABLISHED; + + /** + * Remote address. + * + * @var string + */ + protected $_remoteAddress = ''; + + /** + * Is paused. + * + * @var bool + */ + protected $_isPaused = false; + + /** + * SSL handshake completed or not. + * + * @var bool + */ + protected $_sslHandshakeCompleted = false; + + /** + * All connection instances. + * + * @var array + */ + public static $connections = array(); + + /** + * Status to string. + * + * @var array + */ + public static $_statusToString = array( + self::STATUS_INITIAL => 'INITIAL', + self::STATUS_CONNECTING => 'CONNECTING', + self::STATUS_ESTABLISHED => 'ESTABLISHED', + self::STATUS_CLOSING => 'CLOSING', + self::STATUS_CLOSED => 'CLOSED', + ); + + /** + * Construct. + * + * @param resource $socket + * @param string $remote_address + */ + public function __construct($socket, $remote_address = '') + { + ++self::$statistics['connection_count']; + $this->id = $this->_id = self::$_idRecorder++; + if(self::$_idRecorder === \PHP_INT_MAX){ + self::$_idRecorder = 0; + } + $this->_socket = $socket; + \stream_set_blocking($this->_socket, 0); + // Compatible with hhvm + if (\function_exists('stream_set_read_buffer')) { + \stream_set_read_buffer($this->_socket, 0); + } + Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); + $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; + $this->maxPackageSize = self::$defaultMaxPackageSize; + $this->_remoteAddress = $remote_address; + static::$connections[$this->id] = $this; + } + + /** + * Get status. + * + * @param bool $raw_output + * + * @return int|string + */ + public function getStatus($raw_output = true) + { + if ($raw_output) { + return $this->_status; + } + return self::$_statusToString[$this->_status]; + } + + /** + * Sends data on the connection. + * + * @param mixed $send_buffer + * @param bool $raw + * @return bool|null + */ + public function send($send_buffer, $raw = false) + { + if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) { + return false; + } + + // Try to call protocol::encode($send_buffer) before sending. + if (false === $raw && $this->protocol !== null) { + $parser = $this->protocol; + $send_buffer = $parser::encode($send_buffer, $this); + if ($send_buffer === '') { + return; + } + } + + if ($this->_status !== self::STATUS_ESTABLISHED || + ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) + ) { + if ($this->_sendBuffer && $this->bufferIsFull()) { + ++self::$statistics['send_fail']; + return false; + } + $this->_sendBuffer .= $send_buffer; + $this->checkBufferWillFull(); + return; + } + + // Attempt to send data directly. + if ($this->_sendBuffer === '') { + if ($this->transport === 'ssl') { + Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); + $this->_sendBuffer = $send_buffer; + $this->checkBufferWillFull(); + return; + } + $len = 0; + try { + $len = @\fwrite($this->_socket, $send_buffer); + } catch (\Exception $e) { + Worker::log($e); + } catch (\Error $e) { + Worker::log($e); + } + // send successful. + if ($len === \strlen($send_buffer)) { + $this->bytesWritten += $len; + return true; + } + // Send only part of the data. + if ($len > 0) { + $this->_sendBuffer = \substr($send_buffer, $len); + $this->bytesWritten += $len; + } else { + // Connection closed? + if (!\is_resource($this->_socket) || \feof($this->_socket)) { + ++self::$statistics['send_fail']; + if ($this->onError) { + try { + \call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'client closed'); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + $this->destroy(); + return false; + } + $this->_sendBuffer = $send_buffer; + } + Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); + // Check if the send buffer will be full. + $this->checkBufferWillFull(); + return; + } + + if ($this->bufferIsFull()) { + ++self::$statistics['send_fail']; + return false; + } + + $this->_sendBuffer .= $send_buffer; + // Check if the send buffer is full. + $this->checkBufferWillFull(); + } + + /** + * Get remote IP. + * + * @return string + */ + public function getRemoteIp() + { + $pos = \strrpos($this->_remoteAddress, ':'); + if ($pos) { + return (string) \substr($this->_remoteAddress, 0, $pos); + } + return ''; + } + + /** + * Get remote port. + * + * @return int + */ + public function getRemotePort() + { + if ($this->_remoteAddress) { + return (int) \substr(\strrchr($this->_remoteAddress, ':'), 1); + } + return 0; + } + + /** + * Get remote address. + * + * @return string + */ + public function getRemoteAddress() + { + return $this->_remoteAddress; + } + + /** + * Get local IP. + * + * @return string + */ + public function getLocalIp() + { + $address = $this->getLocalAddress(); + $pos = \strrpos($address, ':'); + if (!$pos) { + return ''; + } + return \substr($address, 0, $pos); + } + + /** + * Get local port. + * + * @return int + */ + public function getLocalPort() + { + $address = $this->getLocalAddress(); + $pos = \strrpos($address, ':'); + if (!$pos) { + return 0; + } + return (int)\substr(\strrchr($address, ':'), 1); + } + + /** + * Get local address. + * + * @return string + */ + public function getLocalAddress() + { + return (string)@\stream_socket_get_name($this->_socket, false); + } + + /** + * Get send buffer queue size. + * + * @return integer + */ + public function getSendBufferQueueSize() + { + return \strlen($this->_sendBuffer); + } + + /** + * Get recv buffer queue size. + * + * @return integer + */ + public function getRecvBufferQueueSize() + { + return \strlen($this->_recvBuffer); + } + + /** + * Is ipv4. + * + * return bool. + */ + public function isIpV4() + { + if ($this->transport === 'unix') { + return false; + } + return \strpos($this->getRemoteIp(), ':') === false; + } + + /** + * Is ipv6. + * + * return bool. + */ + public function isIpV6() + { + if ($this->transport === 'unix') { + return false; + } + return \strpos($this->getRemoteIp(), ':') !== false; + } + + /** + * Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload. + * + * @return void + */ + public function pauseRecv() + { + Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); + $this->_isPaused = true; + } + + /** + * Resumes reading after a call to pauseRecv. + * + * @return void + */ + public function resumeRecv() + { + if ($this->_isPaused === true) { + Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); + $this->_isPaused = false; + $this->baseRead($this->_socket, false); + } + } + + + + /** + * Base read handler. + * + * @param resource $socket + * @param bool $check_eof + * @return void + */ + public function baseRead($socket, $check_eof = true) + { + // SSL handshake. + if ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) { + if ($this->doSslHandshake($socket)) { + $this->_sslHandshakeCompleted = true; + if ($this->_sendBuffer) { + Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); + } + } else { + return; + } + } + + $buffer = ''; + try { + $buffer = @\fread($socket, self::READ_BUFFER_SIZE); + } catch (\Exception $e) {} catch (\Error $e) {} + + // Check connection closed. + if ($buffer === '' || $buffer === false) { + if ($check_eof && (\feof($socket) || !\is_resource($socket) || $buffer === false)) { + $this->destroy(); + return; + } + } else { + $this->bytesRead += \strlen($buffer); + $this->_recvBuffer .= $buffer; + } + + // If the application layer protocol has been set up. + if ($this->protocol !== null) { + $parser = $this->protocol; + while ($this->_recvBuffer !== '' && !$this->_isPaused) { + // The current packet length is known. + if ($this->_currentPackageLength) { + // Data is not enough for a package. + if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) { + break; + } + } else { + // Get current package length. + try { + $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this); + } catch (\Exception $e) {} catch (\Error $e) {} + // The packet length is unknown. + if ($this->_currentPackageLength === 0) { + break; + } elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= $this->maxPackageSize) { + // Data is not enough for a package. + if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) { + break; + } + } // Wrong package. + else { + Worker::safeEcho('Error package. package_length=' . \var_export($this->_currentPackageLength, true)); + $this->destroy(); + return; + } + } + + // The data is enough for a packet. + ++self::$statistics['total_request']; + // The current packet length is equal to the length of the buffer. + if (\strlen($this->_recvBuffer) === $this->_currentPackageLength) { + $one_request_buffer = $this->_recvBuffer; + $this->_recvBuffer = ''; + } else { + // Get a full package from the buffer. + $one_request_buffer = \substr($this->_recvBuffer, 0, $this->_currentPackageLength); + // Remove the current package from the receive buffer. + $this->_recvBuffer = \substr($this->_recvBuffer, $this->_currentPackageLength); + } + // Reset the current packet length to 0. + $this->_currentPackageLength = 0; + if (!$this->onMessage) { + continue; + } + try { + // Decode request buffer before Emitting onMessage callback. + \call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this)); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return; + } + + if ($this->_recvBuffer === '' || $this->_isPaused) { + return; + } + + // Applications protocol is not set. + ++self::$statistics['total_request']; + if (!$this->onMessage) { + $this->_recvBuffer = ''; + return; + } + try { + \call_user_func($this->onMessage, $this, $this->_recvBuffer); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + // Clean receive buffer. + $this->_recvBuffer = ''; + } + + /** + * Base write handler. + * + * @return void|bool + */ + public function baseWrite() + { + \set_error_handler(function(){}); + if ($this->transport === 'ssl') { + $len = @\fwrite($this->_socket, $this->_sendBuffer, 8192); + } else { + $len = @\fwrite($this->_socket, $this->_sendBuffer); + } + \restore_error_handler(); + if ($len === \strlen($this->_sendBuffer)) { + $this->bytesWritten += $len; + Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); + $this->_sendBuffer = ''; + // Try to emit onBufferDrain callback when the send buffer becomes empty. + if ($this->onBufferDrain) { + try { + \call_user_func($this->onBufferDrain, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + if ($this->_status === self::STATUS_CLOSING) { + $this->destroy(); + } + return true; + } + if ($len > 0) { + $this->bytesWritten += $len; + $this->_sendBuffer = \substr($this->_sendBuffer, $len); + } else { + ++self::$statistics['send_fail']; + $this->destroy(); + } + } + + /** + * SSL handshake. + * + * @param $socket + * @return bool + */ + public function doSslHandshake($socket){ + if (\feof($socket)) { + $this->destroy(); + return false; + } + $async = $this instanceof AsyncTcpConnection; + + /** + * We disabled ssl3 because https://blog.qualys.com/ssllabs/2014/10/15/ssl-3-is-dead-killed-by-the-poodle-attack. + * You can enable ssl3 by the codes below. + */ + /*if($async){ + $type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT | STREAM_CRYPTO_METHOD_SSLv23_CLIENT | STREAM_CRYPTO_METHOD_SSLv3_CLIENT; + }else{ + $type = STREAM_CRYPTO_METHOD_SSLv2_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER | STREAM_CRYPTO_METHOD_SSLv3_SERVER; + }*/ + + if($async){ + $type = \STREAM_CRYPTO_METHOD_SSLv2_CLIENT | \STREAM_CRYPTO_METHOD_SSLv23_CLIENT; + }else{ + $type = \STREAM_CRYPTO_METHOD_SSLv2_SERVER | \STREAM_CRYPTO_METHOD_SSLv23_SERVER; + } + + // Hidden error. + \set_error_handler(function($errno, $errstr, $file){ + if (!Worker::$daemonize) { + Worker::safeEcho("SSL handshake error: $errstr \n"); + } + }); + $ret = \stream_socket_enable_crypto($socket, true, $type); + \restore_error_handler(); + // Negotiation has failed. + if (false === $ret) { + $this->destroy(); + return false; + } elseif (0 === $ret) { + // There isn't enough data and should try again. + return 0; + } + if (isset($this->onSslHandshake)) { + try { + \call_user_func($this->onSslHandshake, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return true; + } + + /** + * This method pulls all the data out of a readable stream, and writes it to the supplied destination. + * + * @param self $dest + * @return void + */ + public function pipe(self $dest) + { + $source = $this; + $this->onMessage = function ($source, $data) use ($dest) { + $dest->send($data); + }; + $this->onClose = function ($source) use ($dest) { + $dest->close(); + }; + $dest->onBufferFull = function ($dest) use ($source) { + $source->pauseRecv(); + }; + $dest->onBufferDrain = function ($dest) use ($source) { + $source->resumeRecv(); + }; + } + + /** + * Remove $length of data from receive buffer. + * + * @param int $length + * @return void + */ + public function consumeRecvBuffer($length) + { + $this->_recvBuffer = \substr($this->_recvBuffer, $length); + } + + /** + * Close connection. + * + * @param mixed $data + * @param bool $raw + * @return void + */ + public function close($data = null, $raw = false) + { + if($this->_status === self::STATUS_CONNECTING){ + $this->destroy(); + return; + } + + if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) { + return; + } + + if ($data !== null) { + $this->send($data, $raw); + } + + $this->_status = self::STATUS_CLOSING; + + if ($this->_sendBuffer === '') { + $this->destroy(); + } else { + $this->pauseRecv(); + } + } + + /** + * Get the real socket. + * + * @return resource + */ + public function getSocket() + { + return $this->_socket; + } + + /** + * Check whether the send buffer will be full. + * + * @return void + */ + protected function checkBufferWillFull() + { + if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) { + if ($this->onBufferFull) { + try { + \call_user_func($this->onBufferFull, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } + } + + /** + * Whether send buffer is full. + * + * @return bool + */ + protected function bufferIsFull() + { + // Buffer has been marked as full but still has data to send then the packet is discarded. + if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) { + if ($this->onError) { + try { + \call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return true; + } + return false; + } + + /** + * Whether send buffer is Empty. + * + * @return bool + */ + public function bufferIsEmpty() + { + return empty($this->_sendBuffer); + } + + /** + * Destroy connection. + * + * @return void + */ + public function destroy() + { + // Avoid repeated calls. + if ($this->_status === self::STATUS_CLOSED) { + return; + } + // Remove event listener. + Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); + Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); + + // Close socket. + try { + @\fclose($this->_socket); + } catch (\Exception $e) {} catch (\Error $e) {} + + $this->_status = self::STATUS_CLOSED; + // Try to emit onClose callback. + if ($this->onClose) { + try { + \call_user_func($this->onClose, $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + // Try to emit protocol::onClose + if ($this->protocol && \method_exists($this->protocol, 'onClose')) { + try { + \call_user_func(array($this->protocol, 'onClose'), $this); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + $this->_sendBuffer = $this->_recvBuffer = ''; + $this->_currentPackageLength = 0; + $this->_isPaused = $this->_sslHandshakeCompleted = false; + if ($this->_status === self::STATUS_CLOSED) { + // Cleaning up the callback to avoid memory leaks. + $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null; + // Remove from worker->connections. + if ($this->worker) { + unset($this->worker->connections[$this->_id]); + } + unset(static::$connections[$this->_id]); + } + } + + /** + * Destruct. + * + * @return void + */ + public function __destruct() + { + static $mod; + self::$statistics['connection_count']--; + if (Worker::getGracefulStop()) { + if (!isset($mod)) { + $mod = \ceil((self::$statistics['connection_count'] + 1) / 3); + } + + if (0 === self::$statistics['connection_count'] % $mod) { + Worker::log('worker[' . \posix_getpid() . '] remains ' . self::$statistics['connection_count'] . ' connection(s)'); + } + + if(0 === self::$statistics['connection_count']) { + Worker::stopAll(); + } + } + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Connection/UdpConnection.php b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/UdpConnection.php new file mode 100644 index 0000000..48c0092 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Connection/UdpConnection.php @@ -0,0 +1,191 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +/** + * UdpConnection. + */ +class UdpConnection extends ConnectionInterface +{ + /** + * Application layer protocol. + * The format is like this Workerman\\Protocols\\Http. + * + * @var \Workerman\Protocols\ProtocolInterface + */ + public $protocol = null; + + /** + * Udp socket. + * + * @var resource + */ + protected $_socket = null; + + /** + * Remote address. + * + * @var string + */ + protected $_remoteAddress = ''; + + /** + * Construct. + * + * @param resource $socket + * @param string $remote_address + */ + public function __construct($socket, $remote_address) + { + $this->_socket = $socket; + $this->_remoteAddress = $remote_address; + } + + /** + * Sends data on the connection. + * + * @param string $send_buffer + * @param bool $raw + * @return void|boolean + */ + public function send($send_buffer, $raw = false) + { + if (false === $raw && $this->protocol) { + $parser = $this->protocol; + $send_buffer = $parser::encode($send_buffer, $this); + if ($send_buffer === '') { + return; + } + } + return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress); + } + + /** + * Get remote IP. + * + * @return string + */ + public function getRemoteIp() + { + $pos = \strrpos($this->_remoteAddress, ':'); + if ($pos) { + return \trim(\substr($this->_remoteAddress, 0, $pos), '[]'); + } + return ''; + } + + /** + * Get remote port. + * + * @return int + */ + public function getRemotePort() + { + if ($this->_remoteAddress) { + return (int)\substr(\strrchr($this->_remoteAddress, ':'), 1); + } + return 0; + } + + /** + * Get remote address. + * + * @return string + */ + public function getRemoteAddress() + { + return $this->_remoteAddress; + } + + /** + * Get local IP. + * + * @return string + */ + public function getLocalIp() + { + $address = $this->getLocalAddress(); + $pos = \strrpos($address, ':'); + if (!$pos) { + return ''; + } + return \substr($address, 0, $pos); + } + + /** + * Get local port. + * + * @return int + */ + public function getLocalPort() + { + $address = $this->getLocalAddress(); + $pos = \strrpos($address, ':'); + if (!$pos) { + return 0; + } + return (int)\substr(\strrchr($address, ':'), 1); + } + + /** + * Get local address. + * + * @return string + */ + public function getLocalAddress() + { + return (string)@\stream_socket_get_name($this->_socket, false); + } + + /** + * Is ipv4. + * + * @return bool. + */ + public function isIpV4() + { + if ($this->transport === 'unix') { + return false; + } + return \strpos($this->getRemoteIp(), ':') === false; + } + + /** + * Is ipv6. + * + * @return bool. + */ + public function isIpV6() + { + if ($this->transport === 'unix') { + return false; + } + return \strpos($this->getRemoteIp(), ':') !== false; + } + + /** + * Close connection. + * + * @param mixed $data + * @param bool $raw + * @return bool + */ + public function close($data = null, $raw = false) + { + if ($data !== null) { + $this->send($data, $raw); + } + return true; + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/Ev.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Ev.php new file mode 100644 index 0000000..8dba860 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Ev.php @@ -0,0 +1,195 @@ + + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Workerman\Worker; +use \EvWatcher; + +/** + * ev eventloop + */ +class Ev implements EventInterface +{ + /** + * All listeners for read/write event. + * + * @var array + */ + protected $_allEvents = array(); + + /** + * Event listeners of signal. + * + * @var array + */ + protected $_eventSignal = array(); + + /** + * All timer event listeners. + * [func, args, event, flag, time_interval] + * + * @var array + */ + protected $_eventTimer = array(); + + /** + * Timer id. + * + * @var int + */ + protected static $_timerId = 1; + + /** + * Add a timer. + * {@inheritdoc} + */ + public function add($fd, $flag, $func, $args = null) + { + $callback = function ($event, $socket) use ($fd, $func) { + try { + \call_user_func($func, $fd); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + }; + switch ($flag) { + case self::EV_SIGNAL: + $event = new \EvSignal($fd, $callback); + $this->_eventSignal[$fd] = $event; + return true; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $repeat = $flag === self::EV_TIMER_ONCE ? 0 : $fd; + $param = array($func, (array)$args, $flag, $fd, self::$_timerId); + $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param); + $this->_eventTimer[self::$_timerId] = $event; + return self::$_timerId++; + default : + $fd_key = (int)$fd; + $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE; + $event = new \EvIo($fd, $real_flag, $callback); + $this->_allEvents[$fd_key][$flag] = $event; + return true; + } + + } + + /** + * Remove a timer. + * {@inheritdoc} + */ + public function del($fd, $flag) + { + switch ($flag) { + case self::EV_READ: + case self::EV_WRITE: + $fd_key = (int)$fd; + if (isset($this->_allEvents[$fd_key][$flag])) { + $this->_allEvents[$fd_key][$flag]->stop(); + unset($this->_allEvents[$fd_key][$flag]); + } + if (empty($this->_allEvents[$fd_key])) { + unset($this->_allEvents[$fd_key]); + } + break; + case self::EV_SIGNAL: + $fd_key = (int)$fd; + if (isset($this->_eventSignal[$fd_key])) { + $this->_eventSignal[$fd_key]->stop(); + unset($this->_eventSignal[$fd_key]); + } + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + if (isset($this->_eventTimer[$fd])) { + $this->_eventTimer[$fd]->stop(); + unset($this->_eventTimer[$fd]); + } + break; + } + return true; + } + + /** + * Timer callback. + * + * @param EvWatcher $event + */ + public function timerCallback(EvWatcher $event) + { + $param = $event->data; + $timer_id = $param[4]; + if ($param[2] === self::EV_TIMER_ONCE) { + $this->_eventTimer[$timer_id]->stop(); + unset($this->_eventTimer[$timer_id]); + } + try { + \call_user_func_array($param[0], $param[1]); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + + /** + * Remove all timers. + * + * @return void + */ + public function clearAllTimer() + { + foreach ($this->_eventTimer as $event) { + $event->stop(); + } + $this->_eventTimer = array(); + } + + /** + * Main loop. + * + * @see EventInterface::loop() + */ + public function loop() + { + \Ev::run(); + } + + /** + * Destroy loop. + * + * @return void + */ + public function destroy() + { + foreach ($this->_allEvents as $event) { + $event->stop(); + } + } + + /** + * Get timer count. + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_eventTimer); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/Event.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Event.php new file mode 100644 index 0000000..3e305a1 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Event.php @@ -0,0 +1,217 @@ + + * @copyright 有个鬼<42765633@qq.com> + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Workerman\Worker; + +/** + * libevent eventloop + */ +class Event implements EventInterface +{ + /** + * Event base. + * @var object + */ + protected $_eventBase = null; + + /** + * All listeners for read/write event. + * @var array + */ + protected $_allEvents = array(); + + /** + * Event listeners of signal. + * @var array + */ + protected $_eventSignal = array(); + + /** + * All timer event listeners. + * [func, args, event, flag, time_interval] + * @var array + */ + protected $_eventTimer = array(); + + /** + * Timer id. + * @var int + */ + protected static $_timerId = 1; + + /** + * construct + * @return void + */ + public function __construct() + { + if (\class_exists('\\\\EventBase', false)) { + $class_name = '\\\\EventBase'; + } else { + $class_name = '\EventBase'; + } + $this->_eventBase = new $class_name(); + } + + /** + * @see EventInterface::add() + */ + public function add($fd, $flag, $func, $args=array()) + { + if (\class_exists('\\\\Event', false)) { + $class_name = '\\\\Event'; + } else { + $class_name = '\Event'; + } + switch ($flag) { + case self::EV_SIGNAL: + + $fd_key = (int)$fd; + $event = $class_name::signal($this->_eventBase, $fd, $func); + if (!$event||!$event->add()) { + return false; + } + $this->_eventSignal[$fd_key] = $event; + return true; + + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + + $param = array($func, (array)$args, $flag, $fd, self::$_timerId); + $event = new $class_name($this->_eventBase, -1, $class_name::TIMEOUT|$class_name::PERSIST, array($this, "timerCallback"), $param); + if (!$event||!$event->addTimer($fd)) { + return false; + } + $this->_eventTimer[self::$_timerId] = $event; + return self::$_timerId++; + + default : + $fd_key = (int)$fd; + $real_flag = $flag === self::EV_READ ? $class_name::READ | $class_name::PERSIST : $class_name::WRITE | $class_name::PERSIST; + $event = new $class_name($this->_eventBase, $fd, $real_flag, $func, $fd); + if (!$event||!$event->add()) { + return false; + } + $this->_allEvents[$fd_key][$flag] = $event; + return true; + } + } + + /** + * @see Events\EventInterface::del() + */ + public function del($fd, $flag) + { + switch ($flag) { + + case self::EV_READ: + case self::EV_WRITE: + + $fd_key = (int)$fd; + if (isset($this->_allEvents[$fd_key][$flag])) { + $this->_allEvents[$fd_key][$flag]->del(); + unset($this->_allEvents[$fd_key][$flag]); + } + if (empty($this->_allEvents[$fd_key])) { + unset($this->_allEvents[$fd_key]); + } + break; + + case self::EV_SIGNAL: + $fd_key = (int)$fd; + if (isset($this->_eventSignal[$fd_key])) { + $this->_eventSignal[$fd_key]->del(); + unset($this->_eventSignal[$fd_key]); + } + break; + + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + if (isset($this->_eventTimer[$fd])) { + $this->_eventTimer[$fd]->del(); + unset($this->_eventTimer[$fd]); + } + break; + } + return true; + } + + /** + * Timer callback. + * @param null $fd + * @param int $what + * @param int $timer_id + */ + public function timerCallback($fd, $what, $param) + { + $timer_id = $param[4]; + + if ($param[2] === self::EV_TIMER_ONCE) { + $this->_eventTimer[$timer_id]->del(); + unset($this->_eventTimer[$timer_id]); + } + + try { + \call_user_func_array($param[0], $param[1]); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + + /** + * @see Events\EventInterface::clearAllTimer() + * @return void + */ + public function clearAllTimer() + { + foreach ($this->_eventTimer as $event) { + $event->del(); + } + $this->_eventTimer = array(); + } + + + /** + * @see EventInterface::loop() + */ + public function loop() + { + $this->_eventBase->loop(); + } + + /** + * Destroy loop. + * + * @return void + */ + public function destroy() + { + $this->_eventBase->exit(); + } + + /** + * Get timer count. + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_eventTimer); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/EventInterface.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/EventInterface.php new file mode 100644 index 0000000..88f38f2 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/EventInterface.php @@ -0,0 +1,107 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +interface EventInterface +{ + /** + * Read event. + * + * @var int + */ + const EV_READ = 1; + + /** + * Write event. + * + * @var int + */ + const EV_WRITE = 2; + + /** + * Except event + * + * @var int + */ + const EV_EXCEPT = 3; + + /** + * Signal event. + * + * @var int + */ + const EV_SIGNAL = 4; + + /** + * Timer event. + * + * @var int + */ + const EV_TIMER = 8; + + /** + * Timer once event. + * + * @var int + */ + const EV_TIMER_ONCE = 16; + + /** + * Add event listener to event loop. + * + * @param mixed $fd + * @param int $flag + * @param callable $func + * @param mixed $args + * @return bool + */ + public function add($fd, $flag, $func, $args = null); + + /** + * Remove event listener from event loop. + * + * @param mixed $fd + * @param int $flag + * @return bool + */ + public function del($fd, $flag); + + /** + * Remove all timers. + * + * @return void + */ + public function clearAllTimer(); + + /** + * Main loop. + * + * @return void + */ + public function loop(); + + /** + * Destroy loop. + * + * @return mixed + */ + public function destroy(); + + /** + * Get Timer count. + * + * @return mixed + */ + public function getTimerCount(); +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/Libevent.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Libevent.php new file mode 100644 index 0000000..ac3427c --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Libevent.php @@ -0,0 +1,227 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Workerman\Worker; + +/** + * libevent eventloop + */ +class Libevent implements EventInterface +{ + /** + * Event base. + * + * @var resource + */ + protected $_eventBase = null; + + /** + * All listeners for read/write event. + * + * @var array + */ + protected $_allEvents = array(); + + /** + * Event listeners of signal. + * + * @var array + */ + protected $_eventSignal = array(); + + /** + * All timer event listeners. + * [func, args, event, flag, time_interval] + * + * @var array + */ + protected $_eventTimer = array(); + + /** + * construct + */ + public function __construct() + { + $this->_eventBase = \event_base_new(); + } + + /** + * {@inheritdoc} + */ + public function add($fd, $flag, $func, $args = array()) + { + switch ($flag) { + case self::EV_SIGNAL: + $fd_key = (int)$fd; + $real_flag = \EV_SIGNAL | \EV_PERSIST; + $this->_eventSignal[$fd_key] = \event_new(); + if (!\event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) { + return false; + } + if (!\event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) { + return false; + } + if (!\event_add($this->_eventSignal[$fd_key])) { + return false; + } + return true; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $event = \event_new(); + $timer_id = (int)$event; + if (!\event_set($event, 0, \EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) { + return false; + } + + if (!\event_base_set($event, $this->_eventBase)) { + return false; + } + + $time_interval = $fd * 1000000; + if (!\event_add($event, $time_interval)) { + return false; + } + $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval); + return $timer_id; + + default : + $fd_key = (int)$fd; + $real_flag = $flag === self::EV_READ ? \EV_READ | \EV_PERSIST : \EV_WRITE | \EV_PERSIST; + + $event = \event_new(); + + if (!\event_set($event, $fd, $real_flag, $func, null)) { + return false; + } + + if (!\event_base_set($event, $this->_eventBase)) { + return false; + } + + if (!\event_add($event)) { + return false; + } + + $this->_allEvents[$fd_key][$flag] = $event; + + return true; + } + + } + + /** + * {@inheritdoc} + */ + public function del($fd, $flag) + { + switch ($flag) { + case self::EV_READ: + case self::EV_WRITE: + $fd_key = (int)$fd; + if (isset($this->_allEvents[$fd_key][$flag])) { + \event_del($this->_allEvents[$fd_key][$flag]); + unset($this->_allEvents[$fd_key][$flag]); + } + if (empty($this->_allEvents[$fd_key])) { + unset($this->_allEvents[$fd_key]); + } + break; + case self::EV_SIGNAL: + $fd_key = (int)$fd; + if (isset($this->_eventSignal[$fd_key])) { + \event_del($this->_eventSignal[$fd_key]); + unset($this->_eventSignal[$fd_key]); + } + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + // 这里 fd 为timerid + if (isset($this->_eventTimer[$fd])) { + \event_del($this->_eventTimer[$fd][2]); + unset($this->_eventTimer[$fd]); + } + break; + } + return true; + } + + /** + * Timer callback. + * + * @param mixed $_null1 + * @param int $_null2 + * @param mixed $timer_id + */ + protected function timerCallback($_null1, $_null2, $timer_id) + { + if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) { + \event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]); + } + try { + \call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) { + $this->del($timer_id, self::EV_TIMER_ONCE); + } + } + + /** + * {@inheritdoc} + */ + public function clearAllTimer() + { + foreach ($this->_eventTimer as $task_data) { + \event_del($task_data[2]); + } + $this->_eventTimer = array(); + } + + /** + * {@inheritdoc} + */ + public function loop() + { + \event_base_loop($this->_eventBase); + } + + /** + * Destroy loop. + * + * @return void + */ + public function destroy() + { + foreach ($this->_eventSignal as $event) { + \event_del($event); + } + } + + /** + * Get timer count. + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_eventTimer); + } +} + diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/Base.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/Base.php new file mode 100644 index 0000000..a0a33ae --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/Base.php @@ -0,0 +1,264 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events\React; + +use Workerman\Events\EventInterface; +use React\EventLoop\TimerInterface; +use React\EventLoop\LoopInterface; + +/** + * Class StreamSelectLoop + * @package Workerman\Events\React + */ +class Base implements LoopInterface +{ + /** + * @var array + */ + protected $_timerIdMap = array(); + + /** + * @var int + */ + protected $_timerIdIndex = 0; + + /** + * @var array + */ + protected $_signalHandlerMap = array(); + + /** + * @var LoopInterface + */ + protected $_eventLoop = null; + + /** + * Base constructor. + */ + public function __construct() + { + $this->_eventLoop = new \React\EventLoop\StreamSelectLoop(); + } + + /** + * Add event listener to event loop. + * + * @param $fd + * @param $flag + * @param $func + * @param array $args + * @return bool + */ + public function add($fd, $flag, $func, array $args = array()) + { + $args = (array)$args; + switch ($flag) { + case EventInterface::EV_READ: + return $this->addReadStream($fd, $func); + case EventInterface::EV_WRITE: + return $this->addWriteStream($fd, $func); + case EventInterface::EV_SIGNAL: + if (isset($this->_signalHandlerMap[$fd])) { + $this->removeSignal($fd, $this->_signalHandlerMap[$fd]); + } + $this->_signalHandlerMap[$fd] = $func; + return $this->addSignal($fd, $func); + case EventInterface::EV_TIMER: + $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) { + \call_user_func_array($func, $args); + }); + $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; + return $this->_timerIdIndex; + case EventInterface::EV_TIMER_ONCE: + $index = ++$this->_timerIdIndex; + $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) { + $this->del($index,EventInterface::EV_TIMER_ONCE); + \call_user_func_array($func, $args); + }); + $this->_timerIdMap[$index] = $timer_obj; + return $this->_timerIdIndex; + } + return false; + } + + /** + * Remove event listener from event loop. + * + * @param mixed $fd + * @param int $flag + * @return bool + */ + public function del($fd, $flag) + { + switch ($flag) { + case EventInterface::EV_READ: + return $this->removeReadStream($fd); + case EventInterface::EV_WRITE: + return $this->removeWriteStream($fd); + case EventInterface::EV_SIGNAL: + if (!isset($this->_eventLoop[$fd])) { + return false; + } + $func = $this->_eventLoop[$fd]; + unset($this->_eventLoop[$fd]); + return $this->removeSignal($fd, $func); + + case EventInterface::EV_TIMER: + case EventInterface::EV_TIMER_ONCE: + if (isset($this->_timerIdMap[$fd])){ + $timer_obj = $this->_timerIdMap[$fd]; + unset($this->_timerIdMap[$fd]); + $this->cancelTimer($timer_obj); + return true; + } + } + return false; + } + + + /** + * Main loop. + * + * @return void + */ + public function loop() + { + $this->run(); + } + + + /** + * Destroy loop. + * + * @return void + */ + public function destroy() + { + + } + + /** + * Get timer count. + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_timerIdMap); + } + + /** + * @param resource $stream + * @param callable $listener + */ + public function addReadStream($stream, $listener) + { + return $this->_eventLoop->addReadStream($stream, $listener); + } + + /** + * @param resource $stream + * @param callable $listener + */ + public function addWriteStream($stream, $listener) + { + return $this->_eventLoop->addWriteStream($stream, $listener); + } + + /** + * @param resource $stream + */ + public function removeReadStream($stream) + { + return $this->_eventLoop->removeReadStream($stream); + } + + /** + * @param resource $stream + */ + public function removeWriteStream($stream) + { + return $this->_eventLoop->removeWriteStream($stream); + } + + /** + * @param float|int $interval + * @param callable $callback + * @return \React\EventLoop\Timer\Timer|TimerInterface + */ + public function addTimer($interval, $callback) + { + return $this->_eventLoop->addTimer($interval, $callback); + } + + /** + * @param float|int $interval + * @param callable $callback + * @return \React\EventLoop\Timer\Timer|TimerInterface + */ + public function addPeriodicTimer($interval, $callback) + { + return $this->_eventLoop->addPeriodicTimer($interval, $callback); + } + + /** + * @param TimerInterface $timer + */ + public function cancelTimer(TimerInterface $timer) + { + return $this->_eventLoop->cancelTimer($timer); + } + + /** + * @param callable $listener + */ + public function futureTick($listener) + { + return $this->_eventLoop->futureTick($listener); + } + + /** + * @param int $signal + * @param callable $listener + */ + public function addSignal($signal, $listener) + { + return $this->_eventLoop->addSignal($signal, $listener); + } + + /** + * @param int $signal + * @param callable $listener + */ + public function removeSignal($signal, $listener) + { + return $this->_eventLoop->removeSignal($signal, $listener); + } + + /** + * Run. + */ + public function run() + { + return $this->_eventLoop->run(); + } + + /** + * Stop. + */ + public function stop() + { + return $this->_eventLoop->stop(); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtEventLoop.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtEventLoop.php new file mode 100644 index 0000000..3dab25b --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtEventLoop.php @@ -0,0 +1,27 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events\React; + +/** + * Class ExtEventLoop + * @package Workerman\Events\React + */ +class ExtEventLoop extends Base +{ + + public function __construct() + { + $this->_eventLoop = new \React\EventLoop\ExtEventLoop(); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php new file mode 100644 index 0000000..eb02b35 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php @@ -0,0 +1,27 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events\React; +use Workerman\Events\EventInterface; + +/** + * Class ExtLibEventLoop + * @package Workerman\Events\React + */ +class ExtLibEventLoop extends Base +{ + public function __construct() + { + $this->_eventLoop = new \React\EventLoop\ExtLibeventLoop(); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/StreamSelectLoop.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/StreamSelectLoop.php new file mode 100644 index 0000000..7f5f94b --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/React/StreamSelectLoop.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events\React; + +/** + * Class StreamSelectLoop + * @package Workerman\Events\React + */ +class StreamSelectLoop extends Base +{ + public function __construct() + { + $this->_eventLoop = new \React\EventLoop\StreamSelectLoop(); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/Select.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Select.php new file mode 100644 index 0000000..8b7e1af --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Select.php @@ -0,0 +1,339 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +/** + * select eventloop + */ +class Select implements EventInterface +{ + /** + * All listeners for read/write event. + * + * @var array + */ + public $_allEvents = array(); + + /** + * Event listeners of signal. + * + * @var array + */ + public $_signalEvents = array(); + + /** + * Fds waiting for read event. + * + * @var array + */ + protected $_readFds = array(); + + /** + * Fds waiting for write event. + * + * @var array + */ + protected $_writeFds = array(); + + /** + * Fds waiting for except event. + * + * @var array + */ + protected $_exceptFds = array(); + + /** + * Timer scheduler. + * {['data':timer_id, 'priority':run_timestamp], ..} + * + * @var \SplPriorityQueue + */ + protected $_scheduler = null; + + /** + * All timer event listeners. + * [[func, args, flag, timer_interval], ..] + * + * @var array + */ + protected $_eventTimer = array(); + + /** + * Timer id. + * + * @var int + */ + protected $_timerId = 1; + + /** + * Select timeout. + * + * @var int + */ + protected $_selectTimeout = 100000000; + + /** + * Paired socket channels + * + * @var array + */ + protected $channel = array(); + + /** + * Construct. + */ + public function __construct() + { + // Init SplPriorityQueue. + $this->_scheduler = new \SplPriorityQueue(); + $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + } + + /** + * {@inheritdoc} + */ + public function add($fd, $flag, $func, $args = array()) + { + switch ($flag) { + case self::EV_READ: + case self::EV_WRITE: + $count = $flag === self::EV_READ ? \count($this->_readFds) : \count($this->_writeFds); + if ($count >= 1024) { + echo "Warning: system call select exceeded the maximum number of connections 1024, please install event/libevent extension for more connections.\n"; + } else if (\DIRECTORY_SEPARATOR !== '/' && $count >= 256) { + echo "Warning: system call select exceeded the maximum number of connections 256.\n"; + } + $fd_key = (int)$fd; + $this->_allEvents[$fd_key][$flag] = array($func, $fd); + if ($flag === self::EV_READ) { + $this->_readFds[$fd_key] = $fd; + } else { + $this->_writeFds[$fd_key] = $fd; + } + break; + case self::EV_EXCEPT: + $fd_key = (int)$fd; + $this->_allEvents[$fd_key][$flag] = array($func, $fd); + $this->_exceptFds[$fd_key] = $fd; + break; + case self::EV_SIGNAL: + // Windows not support signal. + if(\DIRECTORY_SEPARATOR !== '/') { + return false; + } + $fd_key = (int)$fd; + $this->_signalEvents[$fd_key][$flag] = array($func, $fd); + \pcntl_signal($fd, array($this, 'signalHandler')); + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $timer_id = $this->_timerId++; + $run_time = \microtime(true) + $fd; + $this->_scheduler->insert($timer_id, -$run_time); + $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd); + $select_timeout = ($run_time - \microtime(true)) * 1000000; + if( $this->_selectTimeout > $select_timeout ){ + $this->_selectTimeout = $select_timeout; + } + return $timer_id; + } + + return true; + } + + /** + * Signal handler. + * + * @param int $signal + */ + public function signalHandler($signal) + { + \call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal)); + } + + /** + * {@inheritdoc} + */ + public function del($fd, $flag) + { + $fd_key = (int)$fd; + switch ($flag) { + case self::EV_READ: + unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]); + if (empty($this->_allEvents[$fd_key])) { + unset($this->_allEvents[$fd_key]); + } + return true; + case self::EV_WRITE: + unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]); + if (empty($this->_allEvents[$fd_key])) { + unset($this->_allEvents[$fd_key]); + } + return true; + case self::EV_EXCEPT: + unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]); + if(empty($this->_allEvents[$fd_key])) + { + unset($this->_allEvents[$fd_key]); + } + return true; + case self::EV_SIGNAL: + if(\DIRECTORY_SEPARATOR !== '/') { + return false; + } + unset($this->_signalEvents[$fd_key]); + \pcntl_signal($fd, SIG_IGN); + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE; + unset($this->_eventTimer[$fd_key]); + return true; + } + return false; + } + + /** + * Tick for timer. + * + * @return void + */ + protected function tick() + { + while (!$this->_scheduler->isEmpty()) { + $scheduler_data = $this->_scheduler->top(); + $timer_id = $scheduler_data['data']; + $next_run_time = -$scheduler_data['priority']; + $time_now = \microtime(true); + $this->_selectTimeout = ($next_run_time - $time_now) * 1000000; + if ($this->_selectTimeout <= 0) { + $this->_scheduler->extract(); + + if (!isset($this->_eventTimer[$timer_id])) { + continue; + } + + // [func, args, flag, timer_interval] + $task_data = $this->_eventTimer[$timer_id]; + if ($task_data[2] === self::EV_TIMER) { + $next_run_time = $time_now + $task_data[3]; + $this->_scheduler->insert($timer_id, -$next_run_time); + } + \call_user_func_array($task_data[0], $task_data[1]); + if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { + $this->del($timer_id, self::EV_TIMER_ONCE); + } + continue; + } + return; + } + $this->_selectTimeout = 100000000; + } + + /** + * {@inheritdoc} + */ + public function clearAllTimer() + { + $this->_scheduler = new \SplPriorityQueue(); + $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + $this->_eventTimer = array(); + } + + /** + * {@inheritdoc} + */ + public function loop() + { + while (1) { + if(\DIRECTORY_SEPARATOR === '/') { + // Calls signal handlers for pending signals + \pcntl_signal_dispatch(); + } + + $read = $this->_readFds; + $write = $this->_writeFds; + $except = $this->_exceptFds; + + if ($read || $write || $except) { + // Waiting read/write/signal/timeout events. + try { + $ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout); + } catch (\Exception $e) {} catch (\Error $e) {} + + } else { + usleep($this->_selectTimeout); + $ret = false; + } + + + if (!$this->_scheduler->isEmpty()) { + $this->tick(); + } + + if (!$ret) { + continue; + } + + if ($read) { + foreach ($read as $fd) { + $fd_key = (int)$fd; + if (isset($this->_allEvents[$fd_key][self::EV_READ])) { + \call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], + array($this->_allEvents[$fd_key][self::EV_READ][1])); + } + } + } + + if ($write) { + foreach ($write as $fd) { + $fd_key = (int)$fd; + if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) { + \call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], + array($this->_allEvents[$fd_key][self::EV_WRITE][1])); + } + } + } + + if($except) { + foreach($except as $fd) { + $fd_key = (int) $fd; + if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) { + \call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0], + array($this->_allEvents[$fd_key][self::EV_EXCEPT][1])); + } + } + } + } + } + + /** + * Destroy loop. + * + * @return void + */ + public function destroy() + { + + } + + /** + * Get timer count. + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_eventTimer); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Events/Swoole.php b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Swoole.php new file mode 100644 index 0000000..ea828eb --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Events/Swoole.php @@ -0,0 +1,222 @@ + + * @link http://www.workerman.net/ + * @link https://github.com/ares333/Workerman + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Swoole\Event; +use Swoole\Timer; + +class Swoole implements EventInterface +{ + + protected $_timer = array(); + + protected $_timerOnceMap = array(); + + protected $mapId = 0; + + protected $_fd = array(); + + // milisecond + public static $signalDispatchInterval = 500; + + protected $_hasSignal = false; + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::add() + */ + public function add($fd, $flag, $func, $args = null) + { + if (! isset($args)) { + $args = array(); + } + switch ($flag) { + case self::EV_SIGNAL: + $res = \pcntl_signal($fd, $func, false); + if (! $this->_hasSignal && $res) { + Timer::tick(static::$signalDispatchInterval, + function () { + \pcntl_signal_dispatch(); + }); + $this->_hasSignal = true; + } + return $res; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $method = self::EV_TIMER === $flag ? 'tick' : 'after'; + if ($this->mapId > \PHP_INT_MAX) { + $this->mapId = 0; + } + $mapId = $this->mapId++; + $timer_id = Timer::$method($fd * 1000, + function ($timer_id = null) use ($func, $args, $mapId) { + \call_user_func_array($func, $args); + // EV_TIMER_ONCE + if (! isset($timer_id)) { + // may be deleted in $func + if (\array_key_exists($mapId, $this->_timerOnceMap)) { + $timer_id = $this->_timerOnceMap[$mapId]; + unset($this->_timer[$timer_id], + $this->_timerOnceMap[$mapId]); + } + } + }); + if ($flag === self::EV_TIMER_ONCE) { + $this->_timerOnceMap[$mapId] = $timer_id; + $this->_timer[$timer_id] = $mapId; + } else { + $this->_timer[$timer_id] = null; + } + return $timer_id; + case self::EV_READ: + case self::EV_WRITE: + $fd_key = (int) $fd; + if (! isset($this->_fd[$fd_key])) { + if ($flag === self::EV_READ) { + $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ); + $fd_type = SWOOLE_EVENT_READ; + } else { + $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE); + $fd_type = SWOOLE_EVENT_WRITE; + } + if ($res) { + $this->_fd[$fd_key] = $fd_type; + } + } else { + $fd_val = $this->_fd[$fd_key]; + $res = true; + if ($flag === self::EV_READ) { + if (($fd_val & SWOOLE_EVENT_READ) !== SWOOLE_EVENT_READ) { + $res = Event::set($fd, $func, null, + SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + $this->_fd[$fd_key] |= SWOOLE_EVENT_READ; + } + } else { + if (($fd_val & SWOOLE_EVENT_WRITE) !== SWOOLE_EVENT_WRITE) { + $res = Event::set($fd, null, $func, + SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + $this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE; + } + } + } + return $res; + } + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::del() + */ + public function del($fd, $flag) + { + switch ($flag) { + case self::EV_SIGNAL: + return \pcntl_signal($fd, SIG_IGN, false); + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + // already remove in EV_TIMER_ONCE callback. + if (! \array_key_exists($fd, $this->_timer)) { + return true; + } + $res = Timer::clear($fd); + if ($res) { + $mapId = $this->_timer[$fd]; + if (isset($mapId)) { + unset($this->_timerOnceMap[$mapId]); + } + unset($this->_timer[$fd]); + } + return $res; + case self::EV_READ: + case self::EV_WRITE: + $fd_key = (int) $fd; + if (isset($this->_fd[$fd_key])) { + $fd_val = $this->_fd[$fd_key]; + if ($flag === self::EV_READ) { + $flag_remove = ~ SWOOLE_EVENT_READ; + } else { + $flag_remove = ~ SWOOLE_EVENT_WRITE; + } + $fd_val &= $flag_remove; + if (0 === $fd_val) { + $res = Event::del($fd); + if ($res) { + unset($this->_fd[$fd_key]); + } + } else { + $res = Event::set($fd, null, null, $fd_val); + if ($res) { + $this->_fd[$fd_key] = $fd_val; + } + } + } else { + $res = true; + } + return $res; + } + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::clearAllTimer() + */ + public function clearAllTimer() + { + foreach (array_keys($this->_timer) as $v) { + Timer::clear($v); + } + $this->_timer = array(); + $this->_timerOnceMap = array(); + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::loop() + */ + public function loop() + { + Event::wait(); + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::destroy() + */ + public function destroy() + { + Event::exit(); + posix_kill(posix_getpid(), SIGINT); + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::getTimerCount() + */ + public function getTimerCount() + { + return \count($this->_timer); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Constants.php b/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Constants.php new file mode 100644 index 0000000..f82a781 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Constants.php @@ -0,0 +1,48 @@ + + * @copyright walkor + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * + * @link http://www.workerman.net/ + */ + +// Display errors. +ini_set('display_errors', 'on'); +// Reporting all. +error_reporting(E_ALL); +// JIT is not stable, temporarily disabled. +ini_set('pcre.jit', 0); + +// For onError callback. +const WORKERMAN_CONNECT_FAIL = 1; +// For onError callback. +const WORKERMAN_SEND_FAIL = 2; + +// Define OS Type +const OS_TYPE_LINUX = 'linux'; +const OS_TYPE_WINDOWS = 'windows'; + +// Compatible with php7 +if (!class_exists('Error')) { + class Error extends Exception + { + } +} + +if (!interface_exists('SessionHandlerInterface')) { + interface SessionHandlerInterface { + public function close(); + public function destroy($session_id); + public function gc($maxlifetime); + public function open($save_path ,$session_name); + public function read($session_id); + public function write($session_id , $session_data); + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Timer.php b/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Timer.php new file mode 100644 index 0000000..b110051 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Lib/Timer.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Lib; + +/** + * Do not use Workerman\Lib\Timer. + * Please use Workerman\Timer. + * This class is only used for compatibility with workerman 3.* + * @package Workerman\Lib + */ +class Timer extends \Workerman\Timer {} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/MIT-LICENSE.txt b/addons/weliam_smartcity/vendor/workerman/workerman/MIT-LICENSE.txt new file mode 100644 index 0000000..fd6b1c8 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2015 walkor and contributors (see https://github.com/walkor/workerman/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Frame.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Frame.php new file mode 100644 index 0000000..26b04de --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Frame.php @@ -0,0 +1,61 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\TcpConnection; + +/** + * Frame Protocol. + */ +class Frame +{ + /** + * Check the integrity of the package. + * + * @param string $buffer + * @param TcpConnection $connection + * @return int + */ + public static function input($buffer, TcpConnection $connection) + { + if (\strlen($buffer) < 4) { + return 0; + } + $unpack_data = \unpack('Ntotal_length', $buffer); + return $unpack_data['total_length']; + } + + /** + * Decode. + * + * @param string $buffer + * @return string + */ + public static function decode($buffer) + { + return \substr($buffer, 4); + } + + /** + * Encode. + * + * @param string $buffer + * @return string + */ + public static function encode($buffer) + { + $total_length = 4 + \strlen($buffer); + return \pack('N', $total_length) . $buffer; + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http.php new file mode 100644 index 0000000..441e5e7 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http.php @@ -0,0 +1,326 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\TcpConnection; +use Workerman\Protocols\Http\Request; +use Workerman\Protocols\Http\Response; +use Workerman\Protocols\Websocket; +use Workerman\Worker; + +/** + * Class Http. + * @package Workerman\Protocols + */ +class Http +{ + /** + * Request class name. + * + * @var string + */ + protected static $_requestClass = 'Workerman\Protocols\Http\Request'; + + /** + * Session name. + * + * @var string + */ + protected static $_sessionName = 'PHPSID'; + + /** + * Upload tmp dir. + * + * @var string + */ + protected static $_uploadTmpDir = ''; + + /** + * Open cache. + * + * @var bool. + */ + protected static $_enableCache = true; + + /** + * Get or set session name. + * + * @param null $name + * @return string + */ + public static function sessionName($name = null) + { + if ($name !== null && $name !== '') { + static::$_sessionName = (string)$name; + } + return static::$_sessionName; + } + + /** + * Get or set the request class name. + * + * @param null $class_name + * @return string + */ + public static function requestClass($class_name = null) + { + if ($class_name) { + static::$_requestClass = $class_name; + } + return static::$_requestClass; + } + + /** + * Enable or disable Cache. + * + * @param $value + */ + public static function enableCache($value) + { + static::$_enableCache = (bool)$value; + } + + /** + * Check the integrity of the package. + * + * @param string $recv_buffer + * @param TcpConnection $connection + * @return int + */ + public static function input($recv_buffer, TcpConnection $connection) + { + static $input = array(); + if (!isset($recv_buffer[512]) && isset($input[$recv_buffer])) { + return $input[$recv_buffer]; + } + $crlf_pos = \strpos($recv_buffer, "\r\n\r\n"); + if (false === $crlf_pos) { + // Judge whether the package length exceeds the limit. + if ($recv_len = \strlen($recv_buffer) >= 16384) { + $connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n"); + return 0; + } + return 0; + } + + $head_len = $crlf_pos + 4; + $method = \strstr($recv_buffer, ' ', true); + + if ($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD' || $method === 'DELETE') { + if (!isset($recv_buffer[512])) { + $input[$recv_buffer] = $head_len; + if (\count($input) > 512) { + unset($input[key($input)]); + } + } + return $head_len; + } else if ($method !== 'POST' && $method !== 'PUT') { + $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true); + return 0; + } + + $header = \substr($recv_buffer, 0, $crlf_pos); + $length = false; + if ($pos = \strpos($header, "\r\nContent-Length: ")) { + $length = $head_len + (int)\substr($header, $pos + 18, 10); + } else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) { + $length = $head_len + $match[1]; + } + + if ($length !== false) { + if (!isset($recv_buffer[512])) { + $input[$recv_buffer] = $length; + if (\count($input) > 512) { + unset($input[key($input)]); + } + } + return $length; + } + + $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true); + return 0; + } + + /** + * Http decode. + * + * @param string $recv_buffer + * @param TcpConnection $connection + * @return \Workerman\Protocols\Http\Request + */ + public static function decode($recv_buffer, TcpConnection $connection) + { + static $requests = array(); + $cacheable = static::$_enableCache && !isset($recv_buffer[512]); + if (true === $cacheable && isset($requests[$recv_buffer])) { + $request = $requests[$recv_buffer]; + $request->connection = $connection; + $connection->__request = $request; + $request->properties = array(); + return $request; + } + $request = new static::$_requestClass($recv_buffer); + $request->connection = $connection; + $connection->__request = $request; + if (true === $cacheable) { + $requests[$recv_buffer] = $request; + if (\count($requests) > 512) { + unset($requests[key($requests)]); + } + } + return $request; + } + + /** + * Http encode. + * + * @param string|Response $response + * @param TcpConnection $connection + * @return string + */ + public static function encode($response, TcpConnection $connection) + { + if (isset($connection->__request)) { + $connection->__request->session = null; + $connection->__request->connection = null; + $connection->__request = null; + } + if (!\is_object($response)) { + $ext_header = ''; + if (isset($connection->__header)) { + foreach ($connection->__header as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $ext_header = "$name: $item\r\n"; + } + } else { + $ext_header = "$name: $value\r\n"; + } + } + unset($connection->__header); + } + $body_len = \strlen($response); + return "HTTP/1.1 200 OK\r\nServer: workerman\r\n{$ext_header}Connection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\n\r\n$response"; + } + + if (isset($connection->__header)) { + $response->withHeaders($connection->__header); + unset($connection->__header); + } + + if (isset($response->file)) { + $file = $response->file['file']; + $offset = $response->file['offset']; + $length = $response->file['length']; + $file_size = (int)\filesize($file); + $body_len = $length > 0 ? $length : $file_size - $offset; + $response->withHeaders(array( + 'Content-Length' => $body_len, + 'Accept-Ranges' => 'bytes', + )); + if ($offset || $length) { + $offset_end = $offset + $body_len - 1; + $response->header('Content-Range', "bytes $offset-$offset_end/$file_size"); + } + if ($body_len < 2 * 1024 * 1024) { + $connection->send((string)$response . file_get_contents($file, false, null, $offset, $body_len), true); + return ''; + } + $handler = \fopen($file, 'r'); + if (false === $handler) { + $connection->close(new Response(403, null, '403 Forbidden')); + return ''; + } + $connection->send((string)$response, true); + static::sendStream($connection, $handler, $offset, $length); + return ''; + } + + return (string)$response; + } + + /** + * Send remainder of a stream to client. + * + * @param TcpConnection $connection + * @param $handler + * @param $offset + * @param $length + */ + protected static function sendStream(TcpConnection $connection, $handler, $offset = 0, $length = 0) + { + $connection->bufferFull = false; + if ($offset !== 0) { + \fseek($handler, $offset); + } + $offset_end = $offset + $length; + // Read file content from disk piece by piece and send to client. + $do_write = function () use ($connection, $handler, $length, $offset_end) { + // Send buffer not full. + while ($connection->bufferFull === false) { + // Read from disk. + $size = 1024 * 1024; + if ($length !== 0) { + $tell = \ftell($handler); + $remain_size = $offset_end - $tell; + if ($remain_size <= 0) { + fclose($handler); + $connection->onBufferDrain = null; + return; + } + $size = $remain_size > $size ? $size : $remain_size; + } + + $buffer = \fread($handler, $size); + // Read eof. + if ($buffer === '' || $buffer === false) { + fclose($handler); + $connection->onBufferDrain = null; + return; + } + $connection->send($buffer, true); + } + }; + // Send buffer full. + $connection->onBufferFull = function ($connection) { + $connection->bufferFull = true; + }; + // Send buffer drain. + $connection->onBufferDrain = function ($connection) use ($do_write) { + $connection->bufferFull = false; + $do_write(); + }; + $do_write(); + } + + /** + * Set or get uploadTmpDir. + * + * @return bool|string + */ + public static function uploadTmpDir($dir = null) + { + if (null !== $dir) { + static::$_uploadTmpDir = $dir; + } + if (static::$_uploadTmpDir === '') { + if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) { + static::$_uploadTmpDir = $upload_tmp_dir; + } else if ($upload_tmp_dir = \sys_get_temp_dir()) { + static::$_uploadTmpDir = $upload_tmp_dir; + } + } + return static::$_uploadTmpDir; + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Chunk.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Chunk.php new file mode 100644 index 0000000..00f3570 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Chunk.php @@ -0,0 +1,48 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + + +/** + * Class Chunk + * @package Workerman\Protocols\Http + */ +class Chunk +{ + /** + * Chunk buffer. + * + * @var string + */ + protected $_buffer = null; + + /** + * Chunk constructor. + * @param $buffer + */ + public function __construct($buffer) + { + $this->_buffer = $buffer; + } + + /** + * __toString + * + * @return string + */ + public function __toString() + { + return \dechex(\strlen($this->_buffer))."\r\n$this->_buffer\r\n"; + } +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Request.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Request.php new file mode 100644 index 0000000..0484f21 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Request.php @@ -0,0 +1,623 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +use Workerman\Connection\TcpConnection; +use Workerman\Protocols\Http\Session; +use Workerman\Protocols\Http; +use Workerman\Worker; + +/** + * Class Request + * @package Workerman\Protocols\Http + */ +class Request +{ + /** + * Connection. + * + * @var TcpConnection + */ + public $connection = null; + + /** + * Session instance. + * + * @var Session + */ + public $session = null; + + /** + * Properties. + * + * @var array + */ + public $properties = array(); + + /** + * Http buffer. + * + * @var string + */ + protected $_buffer = null; + + /** + * Request data. + * + * @var array + */ + protected $_data = null; + + /** + * Header cache. + * + * @var array + */ + protected static $_headerCache = array(); + + /** + * Get cache. + * + * @var array + */ + protected static $_getCache = array(); + + /** + * Post cache. + * + * @var array + */ + protected static $_postCache = array(); + + /** + * Enable cache. + * + * @var bool + */ + protected static $_enableCache = true; + + + /** + * Request constructor. + * + * @param $buffer + */ + public function __construct($buffer) + { + $this->_buffer = $buffer; + } + + /** + * $_GET. + * + * @param null $name + * @param null $default + * @return mixed|null + */ + public function get($name = null, $default = null) + { + if (!isset($this->_data['get'])) { + $this->parseGet(); + } + if (null === $name) { + return $this->_data['get']; + } + return isset($this->_data['get'][$name]) ? $this->_data['get'][$name] : $default; + } + + /** + * $_POST. + * + * @param $name + * @param null $default + * @return mixed|null + */ + public function post($name = null, $default = null) + { + if (!isset($this->_data['post'])) { + $this->parsePost(); + } + if (null === $name) { + return $this->_data['post']; + } + return isset($this->_data['post'][$name]) ? $this->_data['post'][$name] : $default; + } + + /** + * Get header item by name. + * + * @param null $name + * @param null $default + * @return string|null + */ + public function header($name = null, $default = null) + { + if (!isset($this->_data['headers'])) { + $this->parseHeaders(); + } + if (null === $name) { + return $this->_data['headers']; + } + $name = \strtolower($name); + return isset($this->_data['headers'][$name]) ? $this->_data['headers'][$name] : $default; + } + + /** + * Get cookie item by name. + * + * @param null $name + * @param null $default + * @return string|null + */ + public function cookie($name = null, $default = null) + { + if (!isset($this->_data['cookie'])) { + \parse_str(\str_replace('; ', '&', $this->header('cookie')), $this->_data['cookie']); + } + if ($name === null) { + return $this->_data['cookie']; + } + return isset($this->_data['cookie'][$name]) ? $this->_data['cookie'][$name] : $default; + } + + /** + * Get upload files. + * + * @param null $name + * @return array|null + */ + public function file($name = null) + { + if (!isset($this->_data['files'])) { + $this->parsePost(); + } + if (null === $name) { + return $this->_data['files']; + } + return isset($this->_data['files'][$name]) ? $this->_data['files'][$name] : null; + } + + /** + * Get method. + * + * @return string + */ + public function method() + { + if (!isset($this->_data['method'])) { + $this->parseHeadFirstLine(); + } + return $this->_data['method']; + } + + /** + * Get http protocol version. + * + * @return string. + */ + public function protocolVersion() + { + if (!isset($this->_data['protocolVersion'])) { + $this->parseProtocolVersion(); + } + return $this->_data['protocolVersion']; + } + + /** + * Get host. + * + * @param bool $without_port + * @return string + */ + public function host($without_port = false) + { + $host = $this->header('host'); + if ($without_port && $pos = \strpos($host, ':')) { + return \substr($host, 0, $pos); + } + return $host; + } + + /** + * Get uri. + * + * @return mixed + */ + public function uri() + { + if (!isset($this->_data['uri'])) { + $this->parseHeadFirstLine(); + } + return $this->_data['uri']; + } + + /** + * Get path. + * + * @return mixed + */ + public function path() + { + if (!isset($this->_data['path'])) { + $this->_data['path'] = \parse_url($this->uri(), PHP_URL_PATH); + } + return $this->_data['path']; + } + + /** + * Get query string. + * + * @return mixed + */ + public function queryString() + { + if (!isset($this->_data['query_string'])) { + $this->_data['query_string'] = \parse_url($this->uri(), PHP_URL_QUERY); + } + return $this->_data['query_string']; + } + + /** + * Get session. + * + * @return bool|\Workerman\Protocols\Http\Session + */ + public function session() + { + if ($this->session === null) { + $session_id = $this->sessionId(); + if ($session_id === false) { + return false; + } + $this->session = new Session($session_id); + } + return $this->session; + } + + /** + * Get session id. + * + * @return bool|mixed + */ + public function sessionId() + { + if (!isset($this->_data['sid'])) { + $session_name = Http::sessionName(); + $sid = $this->cookie($session_name); + if ($sid === '' || $sid === null) { + if ($this->connection === null) { + Worker::safeEcho('Request->session() fail, header already send'); + return false; + } + $sid = static::createSessionId(); + $cookie_params = \session_get_cookie_params(); + $this->connection->__header['Set-Cookie'] = array($session_name . '=' . $sid + . (empty($cookie_params['domain']) ? '' : '; Domain=' . $cookie_params['domain']) + . (empty($cookie_params['lifetime']) ? '' : '; Max-Age=' . ($cookie_params['lifetime'] + \time())) + . (empty($cookie_params['path']) ? '' : '; Path=' . $cookie_params['path']) + . (empty($cookie_params['samesite']) ? '' : '; SameSite=' . $cookie_params['samesite']) + . (!$cookie_params['secure'] ? '' : '; Secure') + . (!$cookie_params['httponly'] ? '' : '; HttpOnly')); + } + $this->_data['sid'] = $sid; + } + return $this->_data['sid']; + } + + /** + * Get http raw head. + * + * @return string + */ + public function rawHead() + { + if (!isset($this->_data['head'])) { + $this->_data['head'] = \strstr($this->_buffer, "\r\n\r\n", true); + } + return $this->_data['head']; + } + + /** + * Get http raw body. + * + * @return string + */ + public function rawBody() + { + return \substr($this->_buffer, \strpos($this->_buffer, "\r\n\r\n") + 4); + } + + /** + * Get raw buffer. + * + * @return string + */ + public function rawBuffer() + { + return $this->_buffer; + } + + /** + * Enable or disable cache. + * + * @param $value + */ + public static function enableCache($value) + { + static::$_enableCache = (bool)$value; + } + + /** + * Parse first line of http header buffer. + * + * @return void + */ + protected function parseHeadFirstLine() + { + $first_line = \strstr($this->_buffer, "\r\n", true); + $tmp = \explode(' ', $first_line, 3); + $this->_data['method'] = $tmp[0]; + $this->_data['uri'] = isset($tmp[1]) ? $tmp[1] : '/'; + } + + /** + * Parse protocol version. + * + * @return void + */ + protected function parseProtocolVersion() + { + $first_line = \strstr($this->_buffer, "\r\n", true); + $protoco_version = substr(\strstr($first_line, 'HTTP/'), 5); + $this->_data['protocolVersion'] = $protoco_version ? $protoco_version : '1.0'; + } + + /** + * Parse headers. + * + * @return void + */ + protected function parseHeaders() + { + $this->_data['headers'] = array(); + $raw_head = $this->rawHead(); + $head_buffer = \substr($raw_head, \strpos($raw_head, "\r\n") + 2); + $cacheable = static::$_enableCache && !isset($head_buffer[2048]); + if ($cacheable && isset(static::$_headerCache[$head_buffer])) { + $this->_data['headers'] = static::$_headerCache[$head_buffer]; + return; + } + $head_data = \explode("\r\n", $head_buffer); + foreach ($head_data as $content) { + if (false !== \strpos($content, ':')) { + list($key, $value) = \explode(':', $content, 2); + $this->_data['headers'][\strtolower($key)] = \ltrim($value); + } else { + $this->_data['headers'][\strtolower($content)] = ''; + } + } + if ($cacheable) { + static::$_headerCache[$head_buffer] = $this->_data['headers']; + if (\count(static::$_headerCache) > 128) { + unset(static::$_headerCache[key(static::$_headerCache)]); + } + } + } + + /** + * Parse head. + * + * @return void + */ + protected function parseGet() + { + $query_string = $this->queryString(); + $this->_data['get'] = array(); + if ($query_string === '') { + return; + } + $cacheable = static::$_enableCache && !isset($query_string[1024]); + if ($cacheable && isset(static::$_getCache[$query_string])) { + $this->_data['get'] = static::$_getCache[$query_string]; + return; + } + \parse_str($query_string, $this->_data['get']); + if ($cacheable) { + static::$_getCache[$query_string] = $this->_data['get']; + if (\count(static::$_getCache) > 256) { + unset(static::$_getCache[key(static::$_getCache)]); + } + } + } + + /** + * Parse post. + * + * @return void + */ + protected function parsePost() + { + $body_buffer = $this->rawBody(); + $this->_data['post'] = $this->_data['files'] = array(); + if ($body_buffer === '') { + return; + } + $cacheable = static::$_enableCache && !isset($body_buffer[1024]); + if ($cacheable && isset(static::$_postCache[$body_buffer])) { + $this->_data['post'] = static::$_postCache[$body_buffer]; + return; + } + $content_type = $this->header('content-type', ''); + if (\preg_match('/boundary="?(\S+)"?/', $content_type, $match)) { + $http_post_boundary = '--' . $match[1]; + $this->parseUploadFiles($http_post_boundary); + return; + } + if (\preg_match('/\bjson\b/i', $content_type)) { + $this->_data['post'] = (array) json_decode($body_buffer, true); + } else { + \parse_str($body_buffer, $this->_data['post']); + } + if ($cacheable) { + static::$_postCache[$body_buffer] = $this->_data['post']; + if (\count(static::$_postCache) > 256) { + unset(static::$_postCache[key(static::$_postCache)]); + } + } + } + + /** + * Parse upload files. + * + * @param $http_post_boundary + * @return void + */ + protected function parseUploadFiles($http_post_boundary) + { + $http_body = $this->rawBody(); + $http_body = \substr($http_body, 0, \strlen($http_body) - (\strlen($http_post_boundary) + 4)); + $boundary_data_array = \explode($http_post_boundary . "\r\n", $http_body); + if ($boundary_data_array[0] === '') { + unset($boundary_data_array[0]); + } + $key = -1; + $files = array(); + foreach ($boundary_data_array as $boundary_data_buffer) { + list($boundary_header_buffer, $boundary_value) = \explode("\r\n\r\n", $boundary_data_buffer, 2); + // Remove \r\n from the end of buffer. + $boundary_value = \substr($boundary_value, 0, -2); + $key++; + foreach (\explode("\r\n", $boundary_header_buffer) as $item) { + list($header_key, $header_value) = \explode(": ", $item); + $header_key = \strtolower($header_key); + switch ($header_key) { + case "content-disposition": + // Is file data. + if (\preg_match('/name="(.*?)"; filename="(.*?)"$/i', $header_value, $match)) { + $error = 0; + $tmp_file = ''; + $size = \strlen($boundary_value); + $tmp_upload_dir = HTTP::uploadTmpDir(); + if (!$tmp_upload_dir) { + $error = UPLOAD_ERR_NO_TMP_DIR; + } else { + $tmp_file = \tempnam($tmp_upload_dir, 'workerman.upload.'); + if ($tmp_file === false || false == \file_put_contents($tmp_file, $boundary_value)) { + $error = UPLOAD_ERR_CANT_WRITE; + } + } + // Parse upload files. + $files[$key] = array( + 'key' => $match[1], + 'name' => $match[2], + 'tmp_name' => $tmp_file, + 'size' => $size, + 'error' => $error + ); + break; + } // Is post field. + else { + // Parse $_POST. + if (\preg_match('/name="(.*?)"$/', $header_value, $match)) { + $this->_data['post'][$match[1]] = $boundary_value; + } + } + break; + case "content-type": + // add file_type + $files[$key]['type'] = \trim($header_value); + break; + } + } + } + + foreach ($files as $file) { + $key = $file['key']; + unset($file['key']); + $this->_data['files'][$key] = $file; + } + } + + /** + * Create session id. + * + * @return string + */ + protected static function createSessionId() + { + return \bin2hex(\pack('d', \microtime(true)) . \pack('N', \mt_rand())); + } + + /** + * Setter. + * + * @param $name + * @param $value + * @return void + */ + public function __set($name, $value) + { + $this->properties[$name] = $value; + } + + /** + * Getter. + * + * @param $name + * @return mixed|null + */ + public function __get($name) + { + return isset($this->properties[$name]) ? $this->properties[$name] : null; + } + + /** + * Isset. + * + * @param $name + * @return bool + */ + public function __isset($name) + { + return isset($this->properties[$name]); + } + + /** + * Unset. + * + * @param $name + * @return void + */ + public function __unset($name) + { + unset($this->properties[$name]); + } + + /** + * __destruct. + * + * @return void + */ + public function __destruct() + { + if (isset($this->_data['files'])) { + \clearstatcache(); + foreach ($this->_data['files'] as $item) { + if (\is_file($item['tmp_name'])) { + \unlink($item['tmp_name']); + } + } + } + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Response.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Response.php new file mode 100644 index 0000000..14e8ac1 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Response.php @@ -0,0 +1,447 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +/** + * Class Response + * @package Workerman\Protocols\Http + */ +class Response +{ + /** + * Header data. + * + * @var array + */ + protected $_header = null; + + /** + * Http status. + * + * @var int + */ + protected $_status = null; + + /** + * Http reason. + * + * @var string + */ + protected $_reason = null; + + /** + * Http version. + * + * @var string + */ + protected $_version = '1.1'; + + /** + * Http body. + * + * @var string + */ + protected $_body = null; + + /** + * Mine type map. + * @var array + */ + protected static $_mimeTypeMap = null; + + /** + * Phrases. + * + * @var array + */ + protected static $_phrases = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-status', + 208 => 'Already Reported', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ); + + /** + * Init. + * + * @return void + */ + public static function init() { + static::initMimeTypeMap(); + } + + /** + * Response constructor. + * + * @param int $status + * @param array $headers + * @param string $body + */ + public function __construct( + $status = 200, + $headers = array(), + $body = '' + ) { + $this->_status = $status; + $this->_header = $headers; + $this->_body = $body; + } + + /** + * Set header. + * + * @param $name + * @param $value + * @return $this + */ + public function header($name, $value) { + $this->_header[$name] = $value; + return $this; + } + + /** + * Set header. + * + * @param $name + * @param $value + * @return Response + */ + public function withHeader($name, $value) { + return $this->header($name, $value); + } + + /** + * Set headers. + * + * @param $headers + * @return $this + */ + public function withHeaders($headers) { + $this->_header = \array_merge($this->_header, $headers); + return $this; + } + + /** + * Remove header. + * + * @param $name + * @return $this + */ + public function withoutHeader($name) { + unset($this->_header[$name]); + return $this; + } + + /** + * Get header. + * + * @param $name + * @return null|array|string + */ + public function getHeader($name) { + if (!isset($this->_header[$name])) { + return null; + } + return $this->_header[$name]; + } + + /** + * Get headers. + * + * @return array + */ + public function getHeaders() { + return $this->_header; + } + + /** + * Set status. + * + * @param $code + * @param null $reason_phrase + * @return $this + */ + public function withStatus($code, $reason_phrase = null) { + $this->_status = $code; + $this->_reason = $reason_phrase; + return $this; + } + + /** + * Get status code. + * + * @return int + */ + public function getStatusCode() { + return $this->_status; + } + + /** + * Get reason phrase. + * + * @return string + */ + public function getReasonPhrase() { + return $this->_reason; + } + + /** + * Set protocol version. + * + * @param $version + * @return $this + */ + public function withProtocolVersion($version) { + $this->_version = $version; + return $this; + } + + /** + * Set http body. + * + * @param $body + * @return $this + */ + public function withBody($body) { + $this->_body = $body; + return $this; + } + + /** + * Get http raw body. + */ + public function rawBody() { + return $this->_body; + } + + /** + * Send file. + * + * @param $file + * @param int $offset + * @param int $length + * @return $this + */ + public function withFile($file, $offset = 0, $length = 0) { + if (!\is_file($file)) { + return $this->withStatus(404)->withBody('

404 Not Found

'); + } + $this->file = array('file' => $file, 'offset' => $offset, 'length' => $length); + return $this; + } + + /** + * Set cookie. + * + * @param $name + * @param string $value + * @param int $maxage + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $http_only + * @return $this + */ + public function cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false) + { + $this->_header['Set-Cookie'][] = $name . '=' . \rawurlencode($value) + . (empty($domain) ? '' : '; Domain=' . $domain) + . (empty($max_age) ? '' : '; Max-Age=' . $max_age) + . (empty($path) ? '' : '; Path=' . $path) + . (!$secure ? '' : '; Secure') + . (!$http_only ? '' : '; HttpOnly'); + return $this; + } + + /** + * Create header for file. + * + * @param $file + * @return string + */ + protected function createHeadForFile($file_info) + { + $file = $file_info['file']; + $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status]; + $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n"; + $headers = $this->_header; + if (!isset($headers['Server'])) { + $head .= "Server: workerman\r\n"; + } + foreach ($headers as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $head .= "$name: $item\r\n"; + } + continue; + } + $head .= "$name: $value\r\n"; + } + + if (!isset($headers['Connection'])) { + $head .= "Connection: keep-alive\r\n"; + } + + $file_info = \pathinfo($file); + $extension = isset($file_info['extension']) ? $file_info['extension'] : ''; + $base_name = isset($file_info['basename']) ? $file_info['basename'] : 'unknown'; + if (!isset($headers['Content-Type'])) { + if (isset(self::$_mimeTypeMap[$extension])) { + $head .= "Content-Type: " . self::$_mimeTypeMap[$extension] . "\r\n"; + } else { + $head .= "Content-Type: application/octet-stream\r\n"; + } + } + + if (!isset($headers['Content-Disposition']) && !isset(self::$_mimeTypeMap[$extension])) { + $head .= "Content-Disposition: attachment; filename=\"$base_name\"\r\n"; + } + + if (!isset($headers['Last-Modified'])) { + if ($mtime = \filemtime($file)) { + $head .= 'Last-Modified: '.\date('D, d M Y H:i:s', $mtime) . ' ' . \date_default_timezone_get() ."\r\n"; + } + } + + return "{$head}\r\n"; + } + + /** + * __toString. + * + * @return string + */ + public function __toString() + { + if (isset($this->file)) { + return $this->createHeadForFile($this->file); + } + + $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status]; + $body_len = \strlen($this->_body); + if (empty($this->_header)) { + return "HTTP/{$this->_version} {$this->_status} $reason\r\nServer: workerman\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\nConnection: keep-alive\r\n\r\n{$this->_body}"; + } + + $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n"; + $headers = $this->_header; + if (!isset($headers['Server'])) { + $head .= "Server: workerman\r\n"; + } + foreach ($headers as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $head .= "$name: $item\r\n"; + } + continue; + } + $head .= "$name: $value\r\n"; + } + + if (!isset($headers['Connection'])) { + $head .= "Connection: keep-alive\r\n"; + } + + if (!isset($headers['Content-Type'])) { + $head .= "Content-Type: text/html;charset=utf-8\r\n"; + } else if ($headers['Content-Type'] === 'text/event-stream') { + return $head . $this->_body; + } + + if (!isset($headers['Transfer-Encoding'])) { + $head .= "Content-Length: $body_len\r\n\r\n"; + } else { + return "$head\r\n".dechex($body_len)."\r\n{$this->_body}\r\n"; + } + + // The whole http package + return $head . $this->_body; + } + + /** + * Init mime map. + * + * @return void + */ + public static function initMimeTypeMap() + { + $mime_file = __DIR__ . '/mime.types'; + $items = \file($mime_file, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES); + foreach ($items as $content) { + if (\preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) { + $mime_type = $match[1]; + $extension_var = $match[2]; + $extension_array = \explode(' ', \substr($extension_var, 0, -1)); + foreach ($extension_array as $file_extension) { + static::$_mimeTypeMap[$file_extension] = $mime_type; + } + } + } + } +} +Response::init(); diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php new file mode 100644 index 0000000..7aeafc7 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php @@ -0,0 +1,64 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +/** + * Class ServerSentEvents + * @package Workerman\Protocols\Http + */ +class ServerSentEvents +{ + /** + * Data. + * @var array + */ + protected $_data = null; + + /** + * ServerSentEvents constructor. + * $data for example ['event'=>'ping', 'data' => 'some thing', 'id' => 1000, 'retry' => 5000] + * @param array $data + */ + public function __construct(array $data) + { + $this->_data = $data; + } + + /** + * __toString. + * + * @return string + */ + public function __toString() + { + $buffer = ''; + $data = $this->_data; + if (isset($data[''])) { + $buffer = ": {$data['']}\n"; + } + if (isset($data['event'])) { + $buffer .= "event: {$data['event']}\n"; + } + if (isset($data['data'])) { + $buffer .= 'data: ' . \str_replace("\n", "\ndata: ", $data['data']) . "\n\n"; + } + if (isset($data['id'])) { + $buffer .= "id: {$data['id']}\n"; + } + if (isset($data['retry'])) { + $buffer .= "retry: {$data['retry']}\n"; + } + return $buffer; + } +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session.php new file mode 100644 index 0000000..91d5cf5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session.php @@ -0,0 +1,359 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + + +/** + * Class Session + * @package Workerman\Protocols\Http + */ +class Session +{ + /** + * Session andler class which implements SessionHandlerInterface. + * + * @var string + */ + protected static $_handlerClass = 'Workerman\Protocols\Http\Session\FileSessionHandler'; + + /** + * Parameters of __constructor for session handler class. + * + * @var null + */ + protected static $_handlerConfig = null; + + /** + * Session.gc_probability + * + * @var int + */ + protected static $_sessionGcProbability = 1; + + /** + * Session.gc_divisor + * + * @var int + */ + protected static $_sessionGcDivisor = 1000; + + /** + * Session.gc_maxlifetime + * + * @var int + */ + protected static $_sessionGcMaxLifeTime = 1440; + + /** + * Session handler instance. + * + * @var \SessionHandlerInterface + */ + protected static $_handler = null; + + /** + * Session data. + * + * @var array + */ + protected $_data = array(); + + /** + * Session changed and need to save. + * + * @var bool + */ + protected $_needSave = false; + + /** + * Session id. + * + * @var null + */ + protected $_sessionId = null; + + /** + * Session constructor. + * + * @param $session_id + */ + public function __construct($session_id) + { + static::checkSessionId($session_id); + if (static::$_handler === null) { + static::initHandler(); + } + $this->_sessionId = $session_id; + if ($data = static::$_handler->read($session_id)) { + $this->_data = \unserialize($data); + } + } + + /** + * Get session id. + * + * @return string + */ + public function getId() + { + return $this->_sessionId; + } + + /** + * Get session. + * + * @param $name + * @param null $default + * @return mixed|null + */ + public function get($name, $default = null) + { + return isset($this->_data[$name]) ? $this->_data[$name] : $default; + } + + /** + * Store data in the session. + * + * @param $name + * @param $value + */ + public function set($name, $value) + { + $this->_data[$name] = $value; + $this->_needSave = true; + } + + /** + * Delete an item from the session. + * + * @param $name + */ + public function delete($name) + { + unset($this->_data[$name]); + $this->_needSave = true; + } + + /** + * Retrieve and delete an item from the session. + * + * @param $name + * @param null $default + * @return mixed|null + */ + public function pull($name, $default = null) + { + $value = $this->get($name, $default); + $this->delete($name); + return $value; + } + + /** + * Store data in the session. + * + * @param $key + * @param null $value + */ + public function put($key, $value = null) + { + if (!\is_array($key)) { + $this->set($key, $value); + return; + } + + foreach ($key as $k => $v) { + $this->_data[$k] = $v; + } + $this->_needSave = true; + } + + /** + * Remove a piece of data from the session. + * + * @param $name + */ + public function forget($name) + { + if (\is_scalar($name)) { + $this->delete($name); + return; + } + if (\is_array($name)) { + foreach ($name as $key) { + unset($this->_data[$key]); + } + } + $this->_needSave = true; + } + + /** + * Retrieve all the data in the session. + * + * @return array + */ + public function all() + { + return $this->_data; + } + + /** + * Remove all data from the session. + * + * @return void + */ + public function flush() + { + $this->_needSave = true; + $this->_data = array(); + } + + /** + * Determining If An Item Exists In The Session. + * + * @param $name + * @return bool + */ + public function has($name) + { + return isset($this->_data[$name]); + } + + /** + * To determine if an item is present in the session, even if its value is null. + * + * @param $name + * @return bool + */ + public function exists($name) + { + return \array_key_exists($name, $this->_data); + } + + /** + * Save session to store. + * + * @return void + */ + public function save() + { + if ($this->_needSave) { + if (empty($this->_data)) { + static::$_handler->destroy($this->_sessionId); + } else { + static::$_handler->write($this->_sessionId, \serialize($this->_data)); + } + } + $this->_needSave = false; + } + + /** + * Init. + * + * @return void + */ + public static function init() + { + if ($gc_probability = \ini_get('session.gc_probability')) { + self::$_sessionGcProbability = (int)$gc_probability; + } + + if ($gc_divisor = \ini_get('session.gc_divisor')) { + self::$_sessionGcDivisor = (int)$gc_divisor; + } + + if ($gc_max_life_time = \ini_get('session.gc_maxlifetime')) { + self::$_sessionGcMaxLifeTime = (int)$gc_max_life_time; + } + } + + /** + * Set session handler class. + * + * @param null $class_name + * @param null $config + * @return string + */ + public static function handlerClass($class_name = null, $config = null) + { + if ($class_name) { + static::$_handlerClass = $class_name; + } + if ($config) { + static::$_handlerConfig = $config; + } + return static::$_handlerClass; + } + + /** + * Init handler. + * + * @return void + */ + protected static function initHandler() + { + if (static::$_handlerConfig === null) { + static::$_handler = new static::$_handlerClass(); + } else { + static::$_handler = new static::$_handlerClass(static::$_handlerConfig); + } + } + + /** + * Try GC sessions. + * + * @return void + */ + public function tryGcSessions() + { + if (\rand(1, static::$_sessionGcDivisor) > static::$_sessionGcProbability) { + return; + } + static::$_handler->gc(static::$_sessionGcMaxLifeTime); + } + + /** + * __destruct. + * + * @return void + */ + public function __destruct() + { + $this->save(); + $this->tryGcSessions(); + } + + /** + * Check session id. + * + * @param $session_id + */ + protected static function checkSessionId($session_id) + { + if (!\preg_match('/^[a-zA-Z0-9]+$/', $session_id)) { + throw new SessionException("session_id $session_id is invalid"); + } + } +} + +/** + * Class SessionException + * @package Workerman\Protocols\Http + */ +class SessionException extends \RuntimeException +{ + +} + +// Init session. +Session::init(); \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php new file mode 100644 index 0000000..9baa34f --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php @@ -0,0 +1,153 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http\Session; + +/** + * Class FileSessionHandler + * @package Workerman\Protocols\Http\Session + */ +class FileSessionHandler implements \SessionHandlerInterface +{ + /** + * Session save path. + * + * @var string + */ + protected static $_sessionSavePath = null; + + /** + * Session file prefix. + * + * @var string + */ + protected static $_sessionFilePrefix = 'session_'; + + /** + * Init. + */ + public static function init() { + $save_path = @\session_save_path(); + if (!$save_path || \strpos($save_path, 'tcp://') === 0) { + $save_path = \sys_get_temp_dir(); + } + static::sessionSavePath($save_path); + } + + /** + * FileSessionHandler constructor. + * @param array $config + */ + public function __construct($config = array()) { + if (isset($config['save_path'])) { + static::sessionSavePath($config['save_path']); + } + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + $session_file = static::sessionFile($session_id); + \clearstatcache(); + if (\is_file($session_file)) { + $data = \file_get_contents($session_file); + return $data ? $data : ''; + } + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + $temp_file = static::$_sessionSavePath.uniqid(mt_rand(), true); + if (!\file_put_contents($temp_file, $session_data)) { + return false; + } + return \rename($temp_file, static::sessionFile($session_id)); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $session_file = static::sessionFile($session_id); + if (\is_file($session_file)) { + \unlink($session_file); + } + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) { + $time_now = \time(); + foreach (\glob(static::$_sessionSavePath . static::$_sessionFilePrefix . '*') as $file) { + if(\is_file($file) && $time_now - \filemtime($file) > $maxlifetime) { + \unlink($file); + } + } + } + + /** + * Get session file path. + * + * @param $session_id + * @return string + */ + protected static function sessionFile($session_id) { + return static::$_sessionSavePath.static::$_sessionFilePrefix.$session_id; + } + + /** + * Get or set session file path. + * + * @param $path + * @return string + */ + public static function sessionSavePath($path) { + if ($path) { + if ($path[\strlen($path)-1] !== DIRECTORY_SEPARATOR) { + $path .= DIRECTORY_SEPARATOR; + } + static::$_sessionSavePath = $path; + if (!\is_dir($path)) { + \mkdir($path, 0777, true); + } + } + return $path; + } +} + +FileSessionHandler::init(); \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php new file mode 100644 index 0000000..4876ab7 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php @@ -0,0 +1,119 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http\Session; + +/** + * Class RedisSessionHandler + * @package Workerman\Protocols\Http\Session + */ +class RedisSessionHandler extends \SessionHandler +{ + + /** + * @var \Redis + */ + protected $_redis; + + /** + * @var int + */ + protected $_maxLifeTime; + + /** + * RedisSessionHandler constructor. + * @param $config = [ + * 'host' => '127.0.0.1', + * 'port' => 6379, + * 'timeout' => 2, + * 'auth' => '******', + * 'database' => 2, + * 'prefix' => 'redis_session_', + * ] + */ + public function __construct($config) + { + if (false === extension_loaded('redis')) { + throw new \RuntimeException('Please install redis extension.'); + } + $this->_maxLifeTime = (int)ini_get('session.gc_maxlifetime'); + + if (!isset($config['timeout'])) { + $config['timeout'] = 2; + } + + $this->_redis = new \Redis(); + if (false === $this->_redis->connect($config['host'], $config['port'], $config['timeout'])) { + throw new \RuntimeException("Redis connect {$config['host']}:{$config['port']} fail."); + } + if (!empty($config['auth'])) { + $this->_redis->auth($config['auth']); + } + if (!empty($config['database'])) { + $this->_redis->select($config['database']); + } + if (empty($config['prefix'])) { + $config['prefix'] = 'redis_session_'; + } + $this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']); + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + return $this->_redis->get($session_id); + } + + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + return true === $this->_redis->setex($session_id, $this->_maxLifeTime, $session_data); + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $this->_redis->del($session_id); + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return true; + } +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/mime.types b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/mime.types new file mode 100644 index 0000000..e6ccf0a --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Http/mime.types @@ -0,0 +1,90 @@ + +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; + font/ttf ttf; +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/ProtocolInterface.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/ProtocolInterface.php new file mode 100644 index 0000000..4fea87d --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/ProtocolInterface.php @@ -0,0 +1,52 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\ConnectionInterface; + +/** + * Protocol interface + */ +interface ProtocolInterface +{ + /** + * Check the integrity of the package. + * Please return the length of package. + * If length is unknow please return 0 that mean wating more data. + * If the package has something wrong please return false the connection will be closed. + * + * @param string $recv_buffer + * @param ConnectionInterface $connection + * @return int|false + */ + public static function input($recv_buffer, ConnectionInterface $connection); + + /** + * Decode package and emit onMessage($message) callback, $message is the result that decode returned. + * + * @param string $recv_buffer + * @param ConnectionInterface $connection + * @return mixed + */ + public static function decode($recv_buffer, ConnectionInterface $connection); + + /** + * Encode package brefore sending to client. + * + * @param mixed $data + * @param ConnectionInterface $connection + * @return string + */ + public static function encode($data, ConnectionInterface $connection); +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Text.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Text.php new file mode 100644 index 0000000..407ea2d --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Text.php @@ -0,0 +1,70 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\ConnectionInterface; + +/** + * Text Protocol. + */ +class Text +{ + /** + * Check the integrity of the package. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return int + */ + public static function input($buffer, ConnectionInterface $connection) + { + // Judge whether the package length exceeds the limit. + if (isset($connection->maxPackageSize) && \strlen($buffer) >= $connection->maxPackageSize) { + $connection->close(); + return 0; + } + // Find the position of "\n". + $pos = \strpos($buffer, "\n"); + // No "\n", packet length is unknown, continue to wait for the data so return 0. + if ($pos === false) { + return 0; + } + // Return the current package length. + return $pos + 1; + } + + /** + * Encode. + * + * @param string $buffer + * @return string + */ + public static function encode($buffer) + { + // Add "\n" + return $buffer . "\n"; + } + + /** + * Decode. + * + * @param string $buffer + * @return string + */ + public static function decode($buffer) + { + // Remove "\n" + return \rtrim($buffer, "\r\n"); + } +} \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Websocket.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Websocket.php new file mode 100644 index 0000000..f00b147 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Websocket.php @@ -0,0 +1,503 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\ConnectionInterface; +use Workerman\Connection\TcpConnection; +use Workerman\Worker; + +/** + * WebSocket protocol. + */ +class Websocket implements \Workerman\Protocols\ProtocolInterface +{ + /** + * Websocket blob type. + * + * @var string + */ + const BINARY_TYPE_BLOB = "\x81"; + + /** + * Websocket arraybuffer type. + * + * @var string + */ + const BINARY_TYPE_ARRAYBUFFER = "\x82"; + + /** + * Check the integrity of the package. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return int + */ + public static function input($buffer, ConnectionInterface $connection) + { + // Receive length. + $recv_len = \strlen($buffer); + // We need more data. + if ($recv_len < 6) { + return 0; + } + + // Has not yet completed the handshake. + if (empty($connection->websocketHandshake)) { + return static::dealHandshake($buffer, $connection); + } + + // Buffer websocket frame data. + if ($connection->websocketCurrentFrameLength) { + // We need more frame data. + if ($connection->websocketCurrentFrameLength > $recv_len) { + // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1. + return 0; + } + } else { + $firstbyte = \ord($buffer[0]); + $secondbyte = \ord($buffer[1]); + $data_len = $secondbyte & 127; + $is_fin_frame = $firstbyte >> 7; + $masked = $secondbyte >> 7; + + if (!$masked) { + Worker::safeEcho("frame not masked so close the connection\n"); + $connection->close(); + return 0; + } + + $opcode = $firstbyte & 0xf; + switch ($opcode) { + case 0x0: + break; + // Blob type. + case 0x1: + break; + // Arraybuffer type. + case 0x2: + break; + // Close package. + case 0x8: + // Try to emit onWebSocketClose callback. + if (isset($connection->onWebSocketClose) || isset($connection->worker->onWebSocketClose)) { + try { + \call_user_func(isset($connection->onWebSocketClose)?$connection->onWebSocketClose:$connection->worker->onWebSocketClose, $connection); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } // Close connection. + else { + $connection->close("\x88\x02\x03\xe8", true); + } + return 0; + // Ping package. + case 0x9: + break; + // Pong package. + case 0xa: + break; + // Wrong opcode. + default : + Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n"); + $connection->close(); + return 0; + } + + // Calculate packet length. + $head_len = 6; + if ($data_len === 126) { + $head_len = 8; + if ($head_len > $recv_len) { + return 0; + } + $pack = \unpack('nn/ntotal_len', $buffer); + $data_len = $pack['total_len']; + } else { + if ($data_len === 127) { + $head_len = 14; + if ($head_len > $recv_len) { + return 0; + } + $arr = \unpack('n/N2c', $buffer); + $data_len = $arr['c1']*4294967296 + $arr['c2']; + } + } + $current_frame_length = $head_len + $data_len; + + $total_package_size = \strlen($connection->websocketDataBuffer) + $current_frame_length; + if ($total_package_size > $connection->maxPackageSize) { + Worker::safeEcho("error package. package_length=$total_package_size\n"); + $connection->close(); + return 0; + } + + if ($is_fin_frame) { + if ($opcode === 0x9) { + if ($recv_len >= $current_frame_length) { + $ping_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection); + $connection->consumeRecvBuffer($current_frame_length); + $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + if (isset($connection->onWebSocketPing) || isset($connection->worker->onWebSocketPing)) { + try { + \call_user_func(isset($connection->onWebSocketPing)?$connection->onWebSocketPing:$connection->worker->onWebSocketPing, $connection, $ping_data); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } else { + $connection->send($ping_data); + } + $connection->websocketType = $tmp_connection_type; + if ($recv_len > $current_frame_length) { + return static::input(\substr($buffer, $current_frame_length), $connection); + } + } + return 0; + } else if ($opcode === 0xa) { + if ($recv_len >= $current_frame_length) { + $pong_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection); + $connection->consumeRecvBuffer($current_frame_length); + $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + // Try to emit onWebSocketPong callback. + if (isset($connection->onWebSocketPong) || isset($connection->worker->onWebSocketPong)) { + try { + \call_user_func(isset($connection->onWebSocketPong)?$connection->onWebSocketPong:$connection->worker->onWebSocketPong, $connection, $pong_data); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + $connection->websocketType = $tmp_connection_type; + if ($recv_len > $current_frame_length) { + return static::input(\substr($buffer, $current_frame_length), $connection); + } + } + return 0; + } + return $current_frame_length; + } else { + $connection->websocketCurrentFrameLength = $current_frame_length; + } + } + + // Received just a frame length data. + if ($connection->websocketCurrentFrameLength === $recv_len) { + static::decode($buffer, $connection); + $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); + $connection->websocketCurrentFrameLength = 0; + return 0; + } // The length of the received data is greater than the length of a frame. + elseif ($connection->websocketCurrentFrameLength < $recv_len) { + static::decode(\substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection); + $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); + $current_frame_length = $connection->websocketCurrentFrameLength; + $connection->websocketCurrentFrameLength = 0; + // Continue to read next frame. + return static::input(\substr($buffer, $current_frame_length), $connection); + } // The length of the received data is less than the length of a frame. + else { + return 0; + } + } + + /** + * Websocket encode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function encode($buffer, ConnectionInterface $connection) + { + if (!is_scalar($buffer)) { + throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. "); + } + $len = \strlen($buffer); + if (empty($connection->websocketType)) { + $connection->websocketType = static::BINARY_TYPE_BLOB; + } + + $first_byte = $connection->websocketType; + + if ($len <= 125) { + $encode_buffer = $first_byte . \chr($len) . $buffer; + } else { + if ($len <= 65535) { + $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer; + } else { + $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer; + } + } + + // Handshake not completed so temporary buffer websocket data waiting for send. + if (empty($connection->websocketHandshake)) { + if (empty($connection->tmpWebsocketData)) { + $connection->tmpWebsocketData = ''; + } + // If buffer has already full then discard the current package. + if (\strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) { + if ($connection->onError) { + try { + \call_user_func($connection->onError, $connection, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return ''; + } + $connection->tmpWebsocketData .= $encode_buffer; + // Check buffer is full. + if ($connection->maxSendBufferSize <= \strlen($connection->tmpWebsocketData)) { + if ($connection->onBufferFull) { + try { + \call_user_func($connection->onBufferFull, $connection); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } + + // Return empty string. + return ''; + } + + return $encode_buffer; + } + + /** + * Websocket decode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function decode($buffer, ConnectionInterface $connection) + { + $len = \ord($buffer[1]) & 127; + if ($len === 126) { + $masks = \substr($buffer, 4, 4); + $data = \substr($buffer, 8); + } else { + if ($len === 127) { + $masks = \substr($buffer, 10, 4); + $data = \substr($buffer, 14); + } else { + $masks = \substr($buffer, 2, 4); + $data = \substr($buffer, 6); + } + } + $dataLength = \strlen($data); + $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4); + $decoded = $data ^ $masks; + if ($connection->websocketCurrentFrameLength) { + $connection->websocketDataBuffer .= $decoded; + return $connection->websocketDataBuffer; + } else { + if ($connection->websocketDataBuffer !== '') { + $decoded = $connection->websocketDataBuffer . $decoded; + $connection->websocketDataBuffer = ''; + } + return $decoded; + } + } + + /** + * Websocket handshake. + * + * @param string $buffer + * @param TcpConnection $connection + * @return int + */ + public static function dealHandshake($buffer, TcpConnection $connection) + { + // HTTP protocol. + if (0 === \strpos($buffer, 'GET')) { + // Find \r\n\r\n. + $heder_end_pos = \strpos($buffer, "\r\n\r\n"); + if (!$heder_end_pos) { + return 0; + } + $header_length = $heder_end_pos + 4; + + // Get Sec-WebSocket-Key. + $Sec_WebSocket_Key = ''; + if (\preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) { + $Sec_WebSocket_Key = $match[1]; + } else { + $connection->send("HTTP/1.1 200 Websocket\r\nServer: workerman/".Worker::VERSION."\r\n\r\n

Websocket


powered by workerman ".Worker::VERSION."
", + true); + $connection->close(); + return 0; + } + // Calculation websocket key. + $new_key = \base64_encode(\sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)); + // Handshake response data. + $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n" + ."Upgrade: websocket\r\n" + ."Sec-WebSocket-Version: 13\r\n" + ."Connection: Upgrade\r\n" + ."Sec-WebSocket-Accept: " . $new_key . "\r\n"; + + // Websocket data buffer. + $connection->websocketDataBuffer = ''; + // Current websocket frame length. + $connection->websocketCurrentFrameLength = 0; + // Current websocket frame data. + $connection->websocketCurrentFrameBuffer = ''; + // Consume handshake data. + $connection->consumeRecvBuffer($header_length); + + // blob or arraybuffer + if (empty($connection->websocketType)) { + $connection->websocketType = static::BINARY_TYPE_BLOB; + } + + $has_server_header = false; + + // Try to emit onWebSocketConnect callback. + if (isset($connection->onWebSocketConnect) || isset($connection->worker->onWebSocketConnect)) { + static::parseHttpHeader($buffer); + try { + \call_user_func(isset($connection->onWebSocketConnect)?$connection->onWebSocketConnect:$connection->worker->onWebSocketConnect, $connection, $buffer); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + if (!empty($_SESSION) && \class_exists('\GatewayWorker\Lib\Context')) { + $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION); + } + $_GET = $_SERVER = $_SESSION = $_COOKIE = array(); + + if (isset($connection->headers)) { + if (\is_array($connection->headers)) { + foreach ($connection->headers as $header) { + if (\strpos($header, 'Server:') === 0) { + $has_server_header = true; + } + $handshake_message .= "$header\r\n"; + } + } else { + $handshake_message .= "$connection->headers\r\n"; + } + } + } + if (!$has_server_header) { + $handshake_message .= "Server: workerman/".Worker::VERSION."\r\n"; + } + $handshake_message .= "\r\n"; + // Send handshake response. + $connection->send($handshake_message, true); + // Mark handshake complete.. + $connection->websocketHandshake = true; + // There are data waiting to be sent. + if (!empty($connection->tmpWebsocketData)) { + $connection->send($connection->tmpWebsocketData, true); + $connection->tmpWebsocketData = ''; + } + if (\strlen($buffer) > $header_length) { + return static::input(\substr($buffer, $header_length), $connection); + } + return 0; + } // Is flash policy-file-request. + elseif (0 === \strpos($buffer, 'send($policy_xml, true); + $connection->consumeRecvBuffer(\strlen($buffer)); + return 0; + } + // Bad websocket handshake request. + $connection->send("HTTP/1.1 200 Websocket\r\nServer: workerman/".Worker::VERSION."\r\n\r\n

Websocket


powered by workerman ".Worker::VERSION."
", + true); + $connection->close(); + return 0; + } + + /** + * Parse http header. + * + * @param string $buffer + * @return void + */ + protected static function parseHttpHeader($buffer) + { + // Parse headers. + list($http_header, ) = \explode("\r\n\r\n", $buffer, 2); + $header_data = \explode("\r\n", $http_header); + + if ($_SERVER) { + $_SERVER = array(); + } + + list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = \explode(' ', + $header_data[0]); + + unset($header_data[0]); + foreach ($header_data as $content) { + // \r\n\r\n + if (empty($content)) { + continue; + } + list($key, $value) = \explode(':', $content, 2); + $key = \str_replace('-', '_', \strtoupper($key)); + $value = \trim($value); + $_SERVER['HTTP_' . $key] = $value; + switch ($key) { + // HTTP_HOST + case 'HOST': + $tmp = \explode(':', $value); + $_SERVER['SERVER_NAME'] = $tmp[0]; + if (isset($tmp[1])) { + $_SERVER['SERVER_PORT'] = $tmp[1]; + } + break; + // cookie + case 'COOKIE': + \parse_str(\str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE); + break; + } + } + + // QUERY_STRING + $_SERVER['QUERY_STRING'] = \parse_url($_SERVER['REQUEST_URI'], \PHP_URL_QUERY); + if ($_SERVER['QUERY_STRING']) { + // $GET + \parse_str($_SERVER['QUERY_STRING'], $_GET); + } else { + $_SERVER['QUERY_STRING'] = ''; + } + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Ws.php b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Ws.php new file mode 100644 index 0000000..7372187 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Protocols/Ws.php @@ -0,0 +1,472 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Worker; +use Workerman\Lib\Timer; +use Workerman\Connection\TcpConnection; +use Workerman\Connection\ConnectionInterface; + +/** + * Websocket protocol for client. + */ +class Ws +{ + /** + * Websocket blob type. + * + * @var string + */ + const BINARY_TYPE_BLOB = "\x81"; + + /** + * Websocket arraybuffer type. + * + * @var string + */ + const BINARY_TYPE_ARRAYBUFFER = "\x82"; + + /** + * Check the integrity of the package. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return int + */ + public static function input($buffer, ConnectionInterface $connection) + { + if (empty($connection->handshakeStep)) { + Worker::safeEcho("recv data before handshake. Buffer:" . \bin2hex($buffer) . "\n"); + return false; + } + // Recv handshake response + if ($connection->handshakeStep === 1) { + return self::dealHandshake($buffer, $connection); + } + $recv_len = \strlen($buffer); + if ($recv_len < 2) { + return 0; + } + // Buffer websocket frame data. + if ($connection->websocketCurrentFrameLength) { + // We need more frame data. + if ($connection->websocketCurrentFrameLength > $recv_len) { + // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1. + return 0; + } + } else { + + $firstbyte = \ord($buffer[0]); + $secondbyte = \ord($buffer[1]); + $data_len = $secondbyte & 127; + $is_fin_frame = $firstbyte >> 7; + $masked = $secondbyte >> 7; + + if ($masked) { + Worker::safeEcho("frame masked so close the connection\n"); + $connection->close(); + return 0; + } + + $opcode = $firstbyte & 0xf; + + switch ($opcode) { + case 0x0: + break; + // Blob type. + case 0x1: + break; + // Arraybuffer type. + case 0x2: + break; + // Close package. + case 0x8: + // Try to emit onWebSocketClose callback. + if (isset($connection->onWebSocketClose)) { + try { + \call_user_func($connection->onWebSocketClose, $connection); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } // Close connection. + else { + $connection->close(); + } + return 0; + // Ping package. + case 0x9: + break; + // Pong package. + case 0xa: + break; + // Wrong opcode. + default : + Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n"); + $connection->close(); + return 0; + } + // Calculate packet length. + if ($data_len === 126) { + if (\strlen($buffer) < 4) { + return 0; + } + $pack = \unpack('nn/ntotal_len', $buffer); + $current_frame_length = $pack['total_len'] + 4; + } else if ($data_len === 127) { + if (\strlen($buffer) < 10) { + return 0; + } + $arr = \unpack('n/N2c', $buffer); + $current_frame_length = $arr['c1']*4294967296 + $arr['c2'] + 10; + } else { + $current_frame_length = $data_len + 2; + } + + $total_package_size = \strlen($connection->websocketDataBuffer) + $current_frame_length; + if ($total_package_size > $connection->maxPackageSize) { + Worker::safeEcho("error package. package_length=$total_package_size\n"); + $connection->close(); + return 0; + } + + if ($is_fin_frame) { + if ($opcode === 0x9) { + if ($recv_len >= $current_frame_length) { + $ping_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection); + $connection->consumeRecvBuffer($current_frame_length); + $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + if (isset($connection->onWebSocketPing)) { + try { + \call_user_func($connection->onWebSocketPing, $connection, $ping_data); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } else { + $connection->send($ping_data); + } + $connection->websocketType = $tmp_connection_type; + if ($recv_len > $current_frame_length) { + return static::input(\substr($buffer, $current_frame_length), $connection); + } + } + return 0; + + } else if ($opcode === 0xa) { + if ($recv_len >= $current_frame_length) { + $pong_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection); + $connection->consumeRecvBuffer($current_frame_length); + $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + // Try to emit onWebSocketPong callback. + if (isset($connection->onWebSocketPong)) { + try { + \call_user_func($connection->onWebSocketPong, $connection, $pong_data); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + $connection->websocketType = $tmp_connection_type; + if ($recv_len > $current_frame_length) { + return static::input(\substr($buffer, $current_frame_length), $connection); + } + } + return 0; + } + return $current_frame_length; + } else { + $connection->websocketCurrentFrameLength = $current_frame_length; + } + } + // Received just a frame length data. + if ($connection->websocketCurrentFrameLength === $recv_len) { + self::decode($buffer, $connection); + $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); + $connection->websocketCurrentFrameLength = 0; + return 0; + } // The length of the received data is greater than the length of a frame. + elseif ($connection->websocketCurrentFrameLength < $recv_len) { + self::decode(\substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection); + $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength); + $current_frame_length = $connection->websocketCurrentFrameLength; + $connection->websocketCurrentFrameLength = 0; + // Continue to read next frame. + return self::input(\substr($buffer, $current_frame_length), $connection); + } // The length of the received data is less than the length of a frame. + else { + return 0; + } + } + + /** + * Websocket encode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function encode($payload, ConnectionInterface $connection) + { + if (empty($connection->websocketType)) { + $connection->websocketType = self::BINARY_TYPE_BLOB; + } + $payload = (string)$payload; + if (empty($connection->handshakeStep)) { + static::sendHandshake($connection); + } + $mask = 1; + $mask_key = "\x00\x00\x00\x00"; + + $pack = ''; + $length = $length_flag = \strlen($payload); + if (65535 < $length) { + $pack = \pack('NN', ($length & 0xFFFFFFFF00000000) >> 32, $length & 0x00000000FFFFFFFF); + $length_flag = 127; + } else if (125 < $length) { + $pack = \pack('n*', $length); + $length_flag = 126; + } + + $head = ($mask << 7) | $length_flag; + $head = $connection->websocketType . \chr($head) . $pack; + + $frame = $head . $mask_key; + // append payload to frame: + $mask_key = \str_repeat($mask_key, \floor($length / 4)) . \substr($mask_key, 0, $length % 4); + $frame .= $payload ^ $mask_key; + if ($connection->handshakeStep === 1) { + // If buffer has already full then discard the current package. + if (\strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) { + if ($connection->onError) { + try { + \call_user_func($connection->onError, $connection, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + return ''; + } + $connection->tmpWebsocketData = $connection->tmpWebsocketData . $frame; + // Check buffer is full. + if ($connection->maxSendBufferSize <= \strlen($connection->tmpWebsocketData)) { + if ($connection->onBufferFull) { + try { + \call_user_func($connection->onBufferFull, $connection); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + } + return ''; + } + return $frame; + } + + /** + * Websocket decode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function decode($bytes, ConnectionInterface $connection) + { + $data_length = \ord($bytes[1]); + + if ($data_length === 126) { + $decoded_data = \substr($bytes, 4); + } else if ($data_length === 127) { + $decoded_data = \substr($bytes, 10); + } else { + $decoded_data = \substr($bytes, 2); + } + if ($connection->websocketCurrentFrameLength) { + $connection->websocketDataBuffer .= $decoded_data; + return $connection->websocketDataBuffer; + } else { + if ($connection->websocketDataBuffer !== '') { + $decoded_data = $connection->websocketDataBuffer . $decoded_data; + $connection->websocketDataBuffer = ''; + } + return $decoded_data; + } + } + + /** + * Send websocket handshake data. + * + * @return void + */ + public static function onConnect($connection) + { + static::sendHandshake($connection); + } + + /** + * Clean + * + * @param $connection + */ + public static function onClose($connection) + { + $connection->handshakeStep = null; + $connection->websocketCurrentFrameLength = 0; + $connection->tmpWebsocketData = ''; + $connection->websocketDataBuffer = ''; + if (!empty($connection->websocketPingTimer)) { + Timer::del($connection->websocketPingTimer); + $connection->websocketPingTimer = null; + } + } + + /** + * Send websocket handshake. + * + * @param TcpConnection $connection + * @return void + */ + public static function sendHandshake(TcpConnection $connection) + { + if (!empty($connection->handshakeStep)) { + return; + } + // Get Host. + $port = $connection->getRemotePort(); + $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port; + // Handshake header. + $connection->websocketSecKey = \base64_encode(\md5(\mt_rand(), true)); + $user_header = isset($connection->headers) ? $connection->headers : + (isset($connection->wsHttpHeader) ? $connection->wsHttpHeader : null); + $user_header_str = ''; + if (!empty($user_header)) { + if (\is_array($user_header)){ + foreach($user_header as $k=>$v){ + $user_header_str .= "$k: $v\r\n"; + } + } else { + $user_header_str .= $user_header; + } + $user_header_str = "\r\n".\trim($user_header_str); + } + $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n". + (!\preg_match("/\nHost:/i", $user_header_str) ? "Host: $host\r\n" : ''). + "Connection: Upgrade\r\n". + "Upgrade: websocket\r\n". + (isset($connection->websocketOrigin) ? "Origin: ".$connection->websocketOrigin."\r\n":''). + (isset($connection->WSClientProtocol)?"Sec-WebSocket-Protocol: ".$connection->WSClientProtocol."\r\n":''). + "Sec-WebSocket-Version: 13\r\n". + "Sec-WebSocket-Key: " . $connection->websocketSecKey . $user_header_str . "\r\n\r\n"; + $connection->send($header, true); + $connection->handshakeStep = 1; + $connection->websocketCurrentFrameLength = 0; + $connection->websocketDataBuffer = ''; + $connection->tmpWebsocketData = ''; + } + + /** + * Websocket handshake. + * + * @param string $buffer + * @param TcpConnection $connection + * @return int + */ + public static function dealHandshake($buffer, TcpConnection $connection) + { + $pos = \strpos($buffer, "\r\n\r\n"); + if ($pos) { + //checking Sec-WebSocket-Accept + if (\preg_match("/Sec-WebSocket-Accept: *(.*?)\r\n/i", $buffer, $match)) { + if ($match[1] !== \base64_encode(\sha1($connection->websocketSecKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true))) { + Worker::safeEcho("Sec-WebSocket-Accept not match. Header:\n" . \substr($buffer, 0, $pos) . "\n"); + $connection->close(); + return 0; + } + } else { + Worker::safeEcho("Sec-WebSocket-Accept not found. Header:\n" . \substr($buffer, 0, $pos) . "\n"); + $connection->close(); + return 0; + } + + // handshake complete + + // Get WebSocket subprotocol (if specified by server) + if (\preg_match("/Sec-WebSocket-Protocol: *(.*?)\r\n/i", $buffer, $match)) { + $connection->WSServerProtocol = \trim($match[1]); + } + + $connection->handshakeStep = 2; + $handshake_response_length = $pos + 4; + // Try to emit onWebSocketConnect callback. + if (isset($connection->onWebSocketConnect)) { + try { + \call_user_func($connection->onWebSocketConnect, $connection, \substr($buffer, 0, $handshake_response_length)); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + } + // Headbeat. + if (!empty($connection->websocketPingInterval)) { + $connection->websocketPingTimer = Timer::add($connection->websocketPingInterval, function() use ($connection){ + if (false === $connection->send(\pack('H*', '898000000000'), true)) { + Timer::del($connection->websocketPingTimer); + $connection->websocketPingTimer = null; + } + }); + } + + $connection->consumeRecvBuffer($handshake_response_length); + if (!empty($connection->tmpWebsocketData)) { + $connection->send($connection->tmpWebsocketData, true); + $connection->tmpWebsocketData = ''; + } + if (\strlen($buffer) > $handshake_response_length) { + return self::input(\substr($buffer, $handshake_response_length), $connection); + } + } + return 0; + } + + public static function WSSetProtocol($connection, $params) { + $connection->WSClientProtocol = $params[0]; + } + + public static function WSGetServerProtocol($connection) { + return (\property_exists($connection, 'WSServerProtocol') ? $connection->WSServerProtocol : null); + } + +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/README.md b/addons/weliam_smartcity/vendor/workerman/workerman/README.md new file mode 100644 index 0000000..81bc7ee --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/README.md @@ -0,0 +1,306 @@ +# Workerman +[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) +[![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman) +[![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman) +[![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman) +[![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman) +[![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman) + +## What is it +Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications. +Workerman supports HTTP, Websocket, SSL and other custom protocols. +Workerman supports event extension. + +## Requires +PHP 5.3 or Higher +A POSIX compatible operating system (Linux, OSX, BSD) +POSIX and PCNTL extensions required +Event extension recommended for better performance + +## Installation + +``` +composer require workerman/workerman +``` + +## Basic Usage + +### A websocket server +```php +count = 4; + +// Emitted when new connection come +$ws_worker->onConnect = function ($connection) { + echo "New connection\n"; +}; + +// Emitted when data received +$ws_worker->onMessage = function ($connection, $data) { + // Send hello $data + $connection->send('Hello ' . $data); +}; + +// Emitted when connection closed +$ws_worker->onClose = function ($connection) { + echo "Connection closed\n"; +}; + +// Run worker +Worker::runAll(); +``` + +### An http server +```php +use Workerman\Worker; + +require_once __DIR__ . '/vendor/autoload.php'; + +// #### http worker #### +$http_worker = new Worker('http://0.0.0.0:2345'); + +// 4 processes +$http_worker->count = 4; + +// Emitted when data received +$http_worker->onMessage = function ($connection, $request) { + //$request->get(); + //$request->post(); + //$request->header(); + //$request->cookie(); + //$requset->session(); + //$request->uri(); + //$request->path(); + //$request->method(); + + // Send data to client + $connection->send("Hello World"); +}; + +// Run all workers +Worker::runAll(); +``` + +### A tcp server +```php +use Workerman\Worker; + +require_once __DIR__ . '/vendor/autoload.php'; + +// #### create socket and listen 1234 port #### +$tcp_worker = new Worker('tcp://0.0.0.0:1234'); + +// 4 processes +$tcp_worker->count = 4; + +// Emitted when new connection come +$tcp_worker->onConnect = function ($connection) { + echo "New Connection\n"; +}; + +// Emitted when data received +$tcp_worker->onMessage = function ($connection, $data) { + // Send data to client + $connection->send("Hello $data \n"); +}; + +// Emitted when connection is closed +$tcp_worker->onClose = function ($connection) { + echo "Connection closed\n"; +}; + +Worker::runAll(); +``` + +### Enable SSL +```php + array( + 'local_cert' => '/your/path/of/server.pem', + 'local_pk' => '/your/path/of/server.key', + 'verify_peer' => false, + ) +); + +// Create a Websocket server with ssl context. +$ws_worker = new Worker('websocket://0.0.0.0:2346', $context); + +// Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://). +// The similar approaches for Https etc. +$ws_worker->transport = 'ssl'; + +$ws_worker->onMessage = function ($connection, $data) { + // Send hello $data + $connection->send('Hello ' . $data); +}; + +Worker::runAll(); +``` + +### Custom protocol +Protocols/MyTextProtocol.php +```php + +namespace Protocols; + +/** + * User defined protocol + * Format Text+"\n" + */ +class MyTextProtocol +{ + public static function input($recv_buffer) + { + // Find the position of the first occurrence of "\n" + $pos = strpos($recv_buffer, "\n"); + + // Not a complete package. Return 0 because the length of package can not be calculated + if ($pos === false) { + return 0; + } + + // Return length of the package + return $pos+1; + } + + public static function decode($recv_buffer) + { + return trim($recv_buffer); + } + + public static function encode($data) + { + return $data . "\n"; + } +} +``` + +```php +use Workerman\Worker; + +require_once __DIR__ . '/vendor/autoload.php'; + +// #### MyTextProtocol worker #### +$text_worker = new Worker('MyTextProtocol://0.0.0.0:5678'); + +$text_worker->onConnect = function ($connection) { + echo "New connection\n"; +}; + +$text_worker->onMessage = function ($connection, $data) { + // Send data to client + $connection->send("Hello world\n"); +}; + +$text_worker->onClose = function ($connection) { + echo "Connection closed\n"; +}; + +// Run all workers +Worker::runAll(); +``` + +### Timer +```php + +use Workerman\Worker; +use Workerman\Timer; + +require_once __DIR__ . '/vendor/autoload.php'; + +$task = new Worker(); +$task->onWorkerStart = function ($task) { + // 2.5 seconds + $time_interval = 2.5; + $timer_id = Timer::add($time_interval, function () { + echo "Timer run\n"; + }); +}; + +// Run all workers +Worker::runAll(); +``` + +### AsyncTcpConnection (tcp/ws/text/frame etc...) +```php + +use Workerman\Worker; +use Workerman\Connection\AsyncTcpConnection; + +require_once __DIR__ . '/vendor/autoload.php'; + +$worker = new Worker(); +$worker->onWorkerStart = function () { + // Websocket protocol for client. + $ws_connection = new AsyncTcpConnection('ws://echo.websocket.org:80'); + $ws_connection->onConnect = function ($connection) { + $connection->send('Hello'); + }; + $ws_connection->onMessage = function ($connection, $data) { + echo "Recv: $data\n"; + }; + $ws_connection->onError = function ($connection, $code, $msg) { + echo "Error: $msg\n"; + }; + $ws_connection->onClose = function ($connection) { + echo "Connection closed\n"; + }; + $ws_connection->connect(); +}; + +Worker::runAll(); +``` + + + +## Available commands +```php start.php start ``` +```php start.php start -d ``` +![workerman start](http://www.workerman.net/img/workerman-start.png) +```php start.php status ``` +![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123) +```php start.php connections``` +```php start.php stop ``` +```php start.php restart ``` +```php start.php reload ``` + +## Documentation + +中文主页:[http://www.workerman.net](http://www.workerman.net) + +中文文档: [http://doc.workerman.net](http://doc.workerman.net) + +Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md) + +# Benchmarks +https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext&l=zik073-1r + + +## Other links with workerman + +[PHPSocket.IO](https://github.com/walkor/phpsocket.io) +[php-socks5](https://github.com/walkor/php-socks5) +[php-http-proxy](https://github.com/walkor/php-http-proxy) + +## Donate + + +## LICENSE + +Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt). diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Timer.php b/addons/weliam_smartcity/vendor/workerman/workerman/Timer.php new file mode 100644 index 0000000..348bb3a --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Timer.php @@ -0,0 +1,213 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman; + +use Workerman\Events\EventInterface; +use Workerman\Worker; +use \Exception; + +/** + * Timer. + * + * example: + * Workerman\Timer::add($time_interval, callback, array($arg1, $arg2..)); + */ +class Timer +{ + /** + * Tasks that based on ALARM signal. + * [ + * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], + * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], + * .. + * ] + * + * @var array + */ + protected static $_tasks = array(); + + /** + * event + * + * @var EventInterface + */ + protected static $_event = null; + + /** + * timer id + * + * @var int + */ + protected static $_timerId = 0; + + /** + * timer status + * [ + * timer_id1 => bool, + * timer_id2 => bool, + * ...................., + * ] + * + * @var array + */ + protected static $_status = array(); + + /** + * Init. + * + * @param EventInterface $event + * @return void + */ + public static function init($event = null) + { + if ($event) { + self::$_event = $event; + return; + } + if (\function_exists('pcntl_signal')) { + \pcntl_signal(\SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false); + } + } + + /** + * ALARM signal handler. + * + * @return void + */ + public static function signalHandle() + { + if (!self::$_event) { + \pcntl_alarm(1); + self::tick(); + } + } + + /** + * Add a timer. + * + * @param float $time_interval + * @param callable $func + * @param mixed $args + * @param bool $persistent + * @return int|bool + */ + public static function add($time_interval, $func, $args = array(), $persistent = true) + { + if ($time_interval <= 0) { + Worker::safeEcho(new Exception("bad time_interval")); + return false; + } + + if ($args === null) { + $args = array(); + } + + if (self::$_event) { + return self::$_event->add($time_interval, + $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); + } + + if (!\is_callable($func)) { + Worker::safeEcho(new Exception("not callable")); + return false; + } + + if (empty(self::$_tasks)) { + \pcntl_alarm(1); + } + + $run_time = \time() + $time_interval; + if (!isset(self::$_tasks[$run_time])) { + self::$_tasks[$run_time] = array(); + } + + self::$_timerId = self::$_timerId == \PHP_INT_MAX ? 1 : ++self::$_timerId; + self::$_status[self::$_timerId] = true; + self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval); + + return self::$_timerId; + } + + + /** + * Tick. + * + * @return void + */ + public static function tick() + { + if (empty(self::$_tasks)) { + \pcntl_alarm(0); + return; + } + $time_now = \time(); + foreach (self::$_tasks as $run_time => $task_data) { + if ($time_now >= $run_time) { + foreach ($task_data as $index => $one_task) { + $task_func = $one_task[0]; + $task_args = $one_task[1]; + $persistent = $one_task[2]; + $time_interval = $one_task[3]; + try { + \call_user_func_array($task_func, $task_args); + } catch (\Exception $e) { + Worker::safeEcho($e); + } + if($persistent && !empty(self::$_status[$index])) { + $new_run_time = \time() + $time_interval; + if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array(); + self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval); + } + } + unset(self::$_tasks[$run_time]); + } + } + } + + /** + * Remove a timer. + * + * @param mixed $timer_id + * @return bool + */ + public static function del($timer_id) + { + if (self::$_event) { + return self::$_event->del($timer_id, EventInterface::EV_TIMER); + } + + foreach(self::$_tasks as $run_time => $task_data) + { + if(array_key_exists($timer_id, $task_data)) unset(self::$_tasks[$run_time][$timer_id]); + } + + if(array_key_exists($timer_id, self::$_status)) unset(self::$_status[$timer_id]); + + return true; + } + + /** + * Remove all timers. + * + * @return void + */ + public static function delAll() + { + self::$_tasks = self::$_status = array(); + \pcntl_alarm(0); + if (self::$_event) { + self::$_event->clearAllTimer(); + } + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/Worker.php b/addons/weliam_smartcity/vendor/workerman/workerman/Worker.php new file mode 100644 index 0000000..1109e30 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/Worker.php @@ -0,0 +1,2563 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman; +require_once __DIR__ . '/Lib/Constants.php'; + +use Workerman\Events\EventInterface; +use Workerman\Connection\ConnectionInterface; +use Workerman\Connection\TcpConnection; +use Workerman\Connection\UdpConnection; +use Workerman\Lib\Timer; +use Workerman\Events\Select; +use \Exception; + +/** + * Worker class + * A container for listening ports + */ +class Worker +{ + /** + * Version. + * + * @var string + */ + const VERSION = '4.0.18'; + + /** + * Status starting. + * + * @var int + */ + const STATUS_STARTING = 1; + + /** + * Status running. + * + * @var int + */ + const STATUS_RUNNING = 2; + + /** + * Status shutdown. + * + * @var int + */ + const STATUS_SHUTDOWN = 4; + + /** + * Status reloading. + * + * @var int + */ + const STATUS_RELOADING = 8; + + /** + * After sending the restart command to the child process KILL_WORKER_TIMER_TIME seconds, + * if the process is still living then forced to kill. + * + * @var int + */ + const KILL_WORKER_TIMER_TIME = 2; + + /** + * Default backlog. Backlog is the maximum length of the queue of pending connections. + * + * @var int + */ + const DEFAULT_BACKLOG = 102400; + /** + * Max udp package size. + * + * @var int + */ + const MAX_UDP_PACKAGE_SIZE = 65535; + + /** + * The safe distance for columns adjacent + * + * @var int + */ + const UI_SAFE_LENGTH = 4; + + /** + * Worker id. + * + * @var int + */ + public $id = 0; + + /** + * Name of the worker processes. + * + * @var string + */ + public $name = 'none'; + + /** + * Number of worker processes. + * + * @var int + */ + public $count = 1; + + /** + * Unix user of processes, needs appropriate privileges (usually root). + * + * @var string + */ + public $user = ''; + + /** + * Unix group of processes, needs appropriate privileges (usually root). + * + * @var string + */ + public $group = ''; + + /** + * reloadable. + * + * @var bool + */ + public $reloadable = true; + + /** + * reuse port. + * + * @var bool + */ + public $reusePort = false; + + /** + * Emitted when worker processes start. + * + * @var callable + */ + public $onWorkerStart = null; + + /** + * Emitted when a socket connection is successfully established. + * + * @var callable + */ + public $onConnect = null; + + /** + * Emitted when data is received. + * + * @var callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Emitted when the send buffer becomes full. + * + * @var callable + */ + public $onBufferFull = null; + + /** + * Emitted when the send buffer becomes empty. + * + * @var callable + */ + public $onBufferDrain = null; + + /** + * Emitted when worker processes stoped. + * + * @var callable + */ + public $onWorkerStop = null; + + /** + * Emitted when worker processes get reload signal. + * + * @var callable + */ + public $onWorkerReload = null; + + /** + * Transport layer protocol. + * + * @var string + */ + public $transport = 'tcp'; + + /** + * Store all connections of clients. + * + * @var array + */ + public $connections = array(); + + /** + * Application layer protocol. + * + * @var string + */ + public $protocol = null; + + /** + * Root path for autoload. + * + * @var string + */ + protected $_autoloadRootPath = ''; + + /** + * Pause accept new connections or not. + * + * @var bool + */ + protected $_pauseAccept = true; + + /** + * Is worker stopping ? + * @var bool + */ + public $stopping = false; + + /** + * Daemonize. + * + * @var bool + */ + public static $daemonize = false; + + /** + * Stdout file. + * + * @var string + */ + public static $stdoutFile = '/dev/null'; + + /** + * The file to store master process PID. + * + * @var string + */ + public static $pidFile = ''; + + /** + * Log file. + * + * @var mixed + */ + public static $logFile = ''; + + /** + * Global event loop. + * + * @var EventInterface + */ + public static $globalEvent = null; + + /** + * Emitted when the master process get reload signal. + * + * @var callable + */ + public static $onMasterReload = null; + + /** + * Emitted when the master process terminated. + * + * @var callable + */ + public static $onMasterStop = null; + + /** + * EventLoopClass + * + * @var string + */ + public static $eventLoopClass = ''; + + /** + * Process title + * + * @var string + */ + public static $processTitle = 'WorkerMan'; + + /** + * The PID of master process. + * + * @var int + */ + protected static $_masterPid = 0; + + /** + * Listening socket. + * + * @var resource + */ + protected $_mainSocket = null; + + /** + * Socket name. The format is like this http://0.0.0.0:80 . + * + * @var string + */ + protected $_socketName = ''; + + /** parse from _socketName avoid parse again in master or worker + * LocalSocket The format is like tcp://0.0.0.0:8080 + * @var string + */ + + protected $_localSocket=null; + + /** + * Context of socket. + * + * @var resource + */ + protected $_context = null; + + /** + * All worker instances. + * + * @var Worker[] + */ + protected static $_workers = array(); + + /** + * All worker processes pid. + * The format is like this [worker_id=>[pid=>pid, pid=>pid, ..], ..] + * + * @var array + */ + protected static $_pidMap = array(); + + /** + * All worker processes waiting for restart. + * The format is like this [pid=>pid, pid=>pid]. + * + * @var array + */ + protected static $_pidsToRestart = array(); + + /** + * Mapping from PID to worker process ID. + * The format is like this [worker_id=>[0=>$pid, 1=>$pid, ..], ..]. + * + * @var array + */ + protected static $_idMap = array(); + + /** + * Current status. + * + * @var int + */ + protected static $_status = self::STATUS_STARTING; + + /** + * Maximum length of the worker names. + * + * @var int + */ + protected static $_maxWorkerNameLength = 12; + + /** + * Maximum length of the socket names. + * + * @var int + */ + protected static $_maxSocketNameLength = 12; + + /** + * Maximum length of the process user names. + * + * @var int + */ + protected static $_maxUserNameLength = 12; + + /** + * Maximum length of the Proto names. + * + * @var int + */ + protected static $_maxProtoNameLength = 4; + + /** + * Maximum length of the Processes names. + * + * @var int + */ + protected static $_maxProcessesNameLength = 9; + + /** + * Maximum length of the Status names. + * + * @var int + */ + protected static $_maxStatusNameLength = 1; + + /** + * The file to store status info of current worker process. + * + * @var string + */ + protected static $_statisticsFile = ''; + + /** + * Start file. + * + * @var string + */ + protected static $_startFile = ''; + + /** + * OS. + * + * @var string + */ + protected static $_OS = \OS_TYPE_LINUX; + + /** + * Processes for windows. + * + * @var array + */ + protected static $_processForWindows = array(); + + /** + * Status info of current worker process. + * + * @var array + */ + protected static $_globalStatistics = array( + 'start_timestamp' => 0, + 'worker_exit_info' => array() + ); + + /** + * Available event loops. + * + * @var array + */ + protected static $_availableEventLoops = array( + 'event' => '\Workerman\Events\Event', + 'libevent' => '\Workerman\Events\Libevent' + ); + + /** + * PHP built-in protocols. + * + * @var array + */ + protected static $_builtinTransports = array( + 'tcp' => 'tcp', + 'udp' => 'udp', + 'unix' => 'unix', + 'ssl' => 'tcp' + ); + + /** + * PHP built-in error types. + * + * @var array + */ + protected static $_errorType = array( + \E_ERROR => 'E_ERROR', // 1 + \E_WARNING => 'E_WARNING', // 2 + \E_PARSE => 'E_PARSE', // 4 + \E_NOTICE => 'E_NOTICE', // 8 + \E_CORE_ERROR => 'E_CORE_ERROR', // 16 + \E_CORE_WARNING => 'E_CORE_WARNING', // 32 + \E_COMPILE_ERROR => 'E_COMPILE_ERROR', // 64 + \E_COMPILE_WARNING => 'E_COMPILE_WARNING', // 128 + \E_USER_ERROR => 'E_USER_ERROR', // 256 + \E_USER_WARNING => 'E_USER_WARNING', // 512 + \E_USER_NOTICE => 'E_USER_NOTICE', // 1024 + \E_STRICT => 'E_STRICT', // 2048 + \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', // 4096 + \E_DEPRECATED => 'E_DEPRECATED', // 8192 + \E_USER_DEPRECATED => 'E_USER_DEPRECATED' // 16384 + ); + + /** + * Graceful stop or not. + * + * @var bool + */ + protected static $_gracefulStop = false; + + /** + * Standard output stream + * @var resource + */ + protected static $_outputStream = null; + + /** + * If $outputStream support decorated + * @var bool + */ + protected static $_outputDecorated = null; + + /** + * Run all worker instances. + * + * @return void + */ + public static function runAll() + { + static::checkSapiEnv(); + static::init(); + static::parseCommand(); + static::daemonize(); + static::initWorkers(); + static::installSignal(); + static::saveMasterPid(); + static::displayUI(); + static::forkWorkers(); + static::resetStd(); + static::monitorWorkers(); + } + + /** + * Check sapi. + * + * @return void + */ + protected static function checkSapiEnv() + { + // Only for cli. + if (\PHP_SAPI !== 'cli') { + exit("Only run in command line mode \n"); + } + if (\DIRECTORY_SEPARATOR === '\\') { + self::$_OS = \OS_TYPE_WINDOWS; + } + } + + /** + * Init. + * + * @return void + */ + protected static function init() + { + \set_error_handler(function($code, $msg, $file, $line){ + Worker::safeEcho("$msg in file $file on line $line\n"); + }); + + // Start file. + $backtrace = \debug_backtrace(); + static::$_startFile = $backtrace[\count($backtrace) - 1]['file']; + + + $unique_prefix = \str_replace('/', '_', static::$_startFile); + + // Pid file. + if (empty(static::$pidFile)) { + static::$pidFile = __DIR__ . "/../$unique_prefix.pid"; + } + + // Log file. + if (empty(static::$logFile)) { + static::$logFile = __DIR__ . '/../workerman.log'; + } + $log_file = (string)static::$logFile; + if (!\is_file($log_file)) { + \touch($log_file); + \chmod($log_file, 0622); + } + + // State. + static::$_status = static::STATUS_STARTING; + + // For statistics. + static::$_globalStatistics['start_timestamp'] = \time(); + static::$_statisticsFile = \sys_get_temp_dir() . "/$unique_prefix.status"; + + // Process title. + static::setProcessTitle(static::$processTitle . ': master process start_file=' . static::$_startFile); + + // Init data for worker id. + static::initId(); + + // Timer init. + Timer::init(); + } + + /** + * Lock. + * + * @return void + */ + protected static function lock() + { + $fd = \fopen(static::$_startFile, 'r'); + if ($fd && !flock($fd, LOCK_EX)) { + static::log('Workerman['.static::$_startFile.'] already running.'); + exit; + } + } + + /** + * Unlock. + * + * @return void + */ + protected static function unlock() + { + $fd = \fopen(static::$_startFile, 'r'); + $fd && flock($fd, \LOCK_UN); + } + + /** + * Init All worker instances. + * + * @return void + */ + protected static function initWorkers() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + foreach (static::$_workers as $worker) { + // Worker name. + if (empty($worker->name)) { + $worker->name = 'none'; + } + + // Get unix user of the worker process. + if (empty($worker->user)) { + $worker->user = static::getCurrentUser(); + } else { + if (\posix_getuid() !== 0 && $worker->user !== static::getCurrentUser()) { + static::log('Warning: You must have the root privileges to change uid and gid.'); + } + } + + // Socket name. + $worker->socket = $worker->getSocketName(); + + // Status name. + $worker->status = ' [OK] '; + + // Get column mapping for UI + foreach(static::getUiColumns() as $column_name => $prop){ + !isset($worker->{$prop}) && $worker->{$prop} = 'NNNN'; + $prop_length = \strlen($worker->{$prop}); + $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + static::$$key = \max(static::$$key, $prop_length); + } + + // Listen. + if (!$worker->reusePort) { + $worker->listen(); + } + } + } + + /** + * Reload all worker instances. + * + * @return void + */ + public static function reloadAllWorkers() + { + static::init(); + static::initWorkers(); + static::displayUI(); + static::$_status = static::STATUS_RELOADING; + } + + /** + * Get all worker instances. + * + * @return array + */ + public static function getAllWorkers() + { + return static::$_workers; + } + + /** + * Get global event-loop instance. + * + * @return EventInterface + */ + public static function getEventLoop() + { + return static::$globalEvent; + } + + /** + * Get main socket resource + * @return resource + */ + public function getMainSocket(){ + return $this->_mainSocket; + } + + /** + * Init idMap. + * return void + */ + protected static function initId() + { + foreach (static::$_workers as $worker_id => $worker) { + $new_id_map = array(); + $worker->count = $worker->count < 1 ? 1 : $worker->count; + for($key = 0; $key < $worker->count; $key++) { + $new_id_map[$key] = isset(static::$_idMap[$worker_id][$key]) ? static::$_idMap[$worker_id][$key] : 0; + } + static::$_idMap[$worker_id] = $new_id_map; + } + } + + /** + * Get unix user of current porcess. + * + * @return string + */ + protected static function getCurrentUser() + { + $user_info = \posix_getpwuid(\posix_getuid()); + return $user_info['name']; + } + + /** + * Display staring UI. + * + * @return void + */ + protected static function displayUI() + { + global $argv; + if (\in_array('-q', $argv)) { + return; + } + if (static::$_OS !== \OS_TYPE_LINUX) { + static::safeEcho("----------------------- WORKERMAN -----------------------------\r\n"); + static::safeEcho('Workerman version:'. static::VERSION. ' PHP version:'. \PHP_VERSION. "\r\n"); + static::safeEcho("------------------------ WORKERS -------------------------------\r\n"); + static::safeEcho("worker listen processes status\r\n"); + return; + } + + //show version + $line_version = 'Workerman version:' . static::VERSION . \str_pad('PHP version:', 22, ' ', \STR_PAD_LEFT) . \PHP_VERSION . \PHP_EOL; + !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', \strlen($line_version)); + $total_length = static::getSingleLineTotalLength(); + $line_one = '' . \str_pad(' WORKERMAN ', $total_length + \strlen(''), '-', \STR_PAD_BOTH) . ''. \PHP_EOL; + $line_two = \str_pad(' WORKERS ' , $total_length + \strlen(''), '-', \STR_PAD_BOTH) . \PHP_EOL; + static::safeEcho($line_one . $line_version . $line_two); + + //Show title + $title = ''; + foreach(static::getUiColumns() as $column_name => $prop){ + $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + //just keep compatible with listen name + $column_name === 'socket' && $column_name = 'listen'; + $title.= "{$column_name}" . \str_pad('', static::$$key + static::UI_SAFE_LENGTH - \strlen($column_name)); + } + $title && static::safeEcho($title . \PHP_EOL); + + //Show content + foreach (static::$_workers as $worker) { + $content = ''; + foreach(static::getUiColumns() as $column_name => $prop){ + $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + \preg_match_all("/(|<\/n>||<\/w>||<\/g>)/is", $worker->{$prop}, $matches); + $place_holder_length = !empty($matches) ? \strlen(\implode('', $matches[0])) : 0; + $content .= \str_pad($worker->{$prop}, static::$$key + static::UI_SAFE_LENGTH + $place_holder_length); + } + $content && static::safeEcho($content . \PHP_EOL); + } + + //Show last line + $line_last = \str_pad('', static::getSingleLineTotalLength(), '-') . \PHP_EOL; + !empty($content) && static::safeEcho($line_last); + + if (static::$daemonize) { + foreach ($argv as $index => $value) { + if ($value == '-d') { + unset($argv[$index]); + } elseif ($value == 'start' || $value == 'restart') { + $argv[$index] = 'stop'; + } + } + static::safeEcho("Input \"php ".implode(' ', $argv)."\" to stop. Start success.\n\n"); + } else { + static::safeEcho("Press Ctrl+C to stop. Start success.\n"); + } + } + + /** + * Get UI columns to be shown in terminal + * + * 1. $column_map: array('ui_column_name' => 'clas_property_name') + * 2. Consider move into configuration in future + * + * @return array + */ + public static function getUiColumns() + { + return array( + 'proto' => 'transport', + 'user' => 'user', + 'worker' => 'name', + 'socket' => 'socket', + 'processes' => 'count', + 'status' => 'status', + ); + } + + /** + * Get single line total length for ui + * + * @return int + */ + public static function getSingleLineTotalLength() + { + $total_length = 0; + + foreach(static::getUiColumns() as $column_name => $prop){ + $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + $total_length += static::$$key + static::UI_SAFE_LENGTH; + } + + //keep beauty when show less colums + !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', 0); + $total_length <= LINE_VERSIOIN_LENGTH && $total_length = LINE_VERSIOIN_LENGTH; + + return $total_length; + } + + /** + * Parse command. + * + * @return void + */ + protected static function parseCommand() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + global $argv; + // Check argv; + $start_file = $argv[0]; + $usage = "Usage: php yourfile [mode]\nCommands: \nstart\t\tStart worker in DEBUG mode.\n\t\tUse mode -d to start in DAEMON mode.\nstop\t\tStop worker.\n\t\tUse mode -g to stop gracefully.\nrestart\t\tRestart workers.\n\t\tUse mode -d to start in DAEMON mode.\n\t\tUse mode -g to stop gracefully.\nreload\t\tReload codes.\n\t\tUse mode -g to reload gracefully.\nstatus\t\tGet worker status.\n\t\tUse mode -d to show live status.\nconnections\tGet worker connections.\n"; + $available_commands = array( + 'start', + 'stop', + 'restart', + 'reload', + 'status', + 'connections', + ); + $available_mode = array( + '-d', + '-g' + ); + $command = $mode = ''; + foreach ($argv as $value) { + if (\in_array($value, $available_commands)) { + $command = $value; + } elseif (\in_array($value, $available_mode)) { + $mode = $value; + } + } + + if (!$command) { + exit($usage); + } + + // Start command. + $mode_str = ''; + if ($command === 'start') { + if ($mode === '-d' || static::$daemonize) { + $mode_str = 'in DAEMON mode'; + } else { + $mode_str = 'in DEBUG mode'; + } + } + static::log("Workerman[$start_file] $command $mode_str"); + + // Get master process PID. + $master_pid = \is_file(static::$pidFile) ? \file_get_contents(static::$pidFile) : 0; + $master_is_alive = $master_pid && \posix_kill($master_pid, 0) && \posix_getpid() !== $master_pid; + // Master is still alive? + if ($master_is_alive) { + if ($command === 'start') { + static::log("Workerman[$start_file] already running"); + exit; + } + } elseif ($command !== 'start' && $command !== 'restart') { + static::log("Workerman[$start_file] not run"); + exit; + } + + // execute command. + switch ($command) { + case 'start': + if ($mode === '-d') { + static::$daemonize = true; + } + break; + case 'status': + while (1) { + if (\is_file(static::$_statisticsFile)) { + @\unlink(static::$_statisticsFile); + } + // Master process will send SIGUSR2 signal to all child processes. + \posix_kill($master_pid, SIGUSR2); + // Sleep 1 second. + \sleep(1); + // Clear terminal. + if ($mode === '-d') { + static::safeEcho("\33[H\33[2J\33(B\33[m", true); + } + // Echo status data. + static::safeEcho(static::formatStatusData()); + if ($mode !== '-d') { + exit(0); + } + static::safeEcho("\nPress Ctrl+C to quit.\n\n"); + } + exit(0); + case 'connections': + if (\is_file(static::$_statisticsFile) && \is_writable(static::$_statisticsFile)) { + \unlink(static::$_statisticsFile); + } + // Master process will send SIGIO signal to all child processes. + \posix_kill($master_pid, SIGIO); + // Waiting amoment. + \usleep(500000); + // Display statisitcs data from a disk file. + if(\is_readable(static::$_statisticsFile)) { + \readfile(static::$_statisticsFile); + } + exit(0); + case 'restart': + case 'stop': + if ($mode === '-g') { + static::$_gracefulStop = true; + $sig = \SIGHUP; + static::log("Workerman[$start_file] is gracefully stopping ..."); + } else { + static::$_gracefulStop = false; + $sig = \SIGINT; + static::log("Workerman[$start_file] is stopping ..."); + } + // Send stop signal to master process. + $master_pid && \posix_kill($master_pid, $sig); + // Timeout. + $timeout = 5; + $start_time = \time(); + // Check master process is still alive? + while (1) { + $master_is_alive = $master_pid && \posix_kill($master_pid, 0); + if ($master_is_alive) { + // Timeout? + if (!static::$_gracefulStop && \time() - $start_time >= $timeout) { + static::log("Workerman[$start_file] stop fail"); + exit; + } + // Waiting amoment. + \usleep(10000); + continue; + } + // Stop success. + static::log("Workerman[$start_file] stop success"); + if ($command === 'stop') { + exit(0); + } + if ($mode === '-d') { + static::$daemonize = true; + } + break; + } + break; + case 'reload': + if($mode === '-g'){ + $sig = \SIGQUIT; + }else{ + $sig = \SIGUSR1; + } + \posix_kill($master_pid, $sig); + exit; + default : + if (isset($command)) { + static::safeEcho('Unknown command: ' . $command . "\n"); + } + exit($usage); + } + } + + /** + * Format status data. + * + * @return string + */ + protected static function formatStatusData() + { + static $total_request_cache = array(); + if (!\is_readable(static::$_statisticsFile)) { + return ''; + } + $info = \file(static::$_statisticsFile, \FILE_IGNORE_NEW_LINES); + if (!$info) { + return ''; + } + $status_str = ''; + $current_total_request = array(); + $worker_info = \unserialize($info[0]); + \ksort($worker_info, SORT_NUMERIC); + unset($info[0]); + $data_waiting_sort = array(); + $read_process_status = false; + $total_requests = 0; + $total_qps = 0; + $total_connections = 0; + $total_fails = 0; + $total_memory = 0; + $total_timers = 0; + $maxLen1 = static::$_maxSocketNameLength; + $maxLen2 = static::$_maxWorkerNameLength; + foreach($info as $key => $value) { + if (!$read_process_status) { + $status_str .= $value . "\n"; + if (\preg_match('/^pid.*?memory.*?listening/', $value)) { + $read_process_status = true; + } + continue; + } + if(\preg_match('/^[0-9]+/', $value, $pid_math)) { + $pid = $pid_math[0]; + $data_waiting_sort[$pid] = $value; + if(\preg_match('/^\S+?\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?/', $value, $match)) { + $total_memory += \intval(\str_ireplace('M','',$match[1])); + $maxLen1 = \max($maxLen1,\strlen($match[2])); + $maxLen2 = \max($maxLen2,\strlen($match[3])); + $total_connections += \intval($match[4]); + $total_fails += \intval($match[5]); + $total_timers += \intval($match[6]); + $current_total_request[$pid] = $match[7]; + $total_requests += \intval($match[7]); + } + } + } + foreach($worker_info as $pid => $info) { + if (!isset($data_waiting_sort[$pid])) { + $status_str .= "$pid\t" . \str_pad('N/A', 7) . " " + . \str_pad($info['listen'], static::$_maxSocketNameLength) . " " + . \str_pad($info['name'], static::$_maxWorkerNameLength) . " " + . \str_pad('N/A', 11) . " " . \str_pad('N/A', 9) . " " + . \str_pad('N/A', 7) . " " . \str_pad('N/A', 13) . " N/A [busy] \n"; + continue; + } + //$qps = isset($total_request_cache[$pid]) ? $current_total_request[$pid] + if (!isset($total_request_cache[$pid]) || !isset($current_total_request[$pid])) { + $qps = 0; + } else { + $qps = $current_total_request[$pid] - $total_request_cache[$pid]; + $total_qps += $qps; + } + $status_str .= $data_waiting_sort[$pid]. " " . \str_pad($qps, 6) ." [idle]\n"; + } + $total_request_cache = $current_total_request; + $status_str .= "----------------------------------------------PROCESS STATUS---------------------------------------------------\n"; + $status_str .= "Summary\t" . \str_pad($total_memory.'M', 7) . " " + . \str_pad('-', $maxLen1) . " " + . \str_pad('-', $maxLen2) . " " + . \str_pad($total_connections, 11) . " " . \str_pad($total_fails, 9) . " " + . \str_pad($total_timers, 7) . " " . \str_pad($total_requests, 13) . " " + . \str_pad($total_qps,6)." [Summary] \n"; + return $status_str; + } + + + /** + * Install signal handler. + * + * @return void + */ + protected static function installSignal() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + $signalHandler = '\Workerman\Worker::signalHandler'; + // stop + \pcntl_signal(\SIGINT, $signalHandler, false); + // stop + \pcntl_signal(\SIGTERM, $signalHandler, false); + // graceful stop + \pcntl_signal(\SIGHUP, $signalHandler, false); + // reload + \pcntl_signal(\SIGUSR1, $signalHandler, false); + // graceful reload + \pcntl_signal(\SIGQUIT, $signalHandler, false); + // status + \pcntl_signal(\SIGUSR2, $signalHandler, false); + // connection status + \pcntl_signal(\SIGIO, $signalHandler, false); + // ignore + \pcntl_signal(\SIGPIPE, \SIG_IGN, false); + } + + /** + * Reinstall signal handler. + * + * @return void + */ + protected static function reinstallSignal() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + $signalHandler = '\Workerman\Worker::signalHandler'; + // uninstall stop signal handler + \pcntl_signal(\SIGINT, \SIG_IGN, false); + // uninstall stop signal handler + \pcntl_signal(\SIGTERM, \SIG_IGN, false); + // uninstall graceful stop signal handler + \pcntl_signal(\SIGHUP, \SIG_IGN, false); + // uninstall reload signal handler + \pcntl_signal(\SIGUSR1, \SIG_IGN, false); + // uninstall graceful reload signal handler + \pcntl_signal(\SIGQUIT, \SIG_IGN, false); + // uninstall status signal handler + \pcntl_signal(\SIGUSR2, \SIG_IGN, false); + // uninstall connections status signal handler + \pcntl_signal(\SIGIO, \SIG_IGN, false); + // reinstall stop signal handler + static::$globalEvent->add(\SIGINT, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful stop signal handler + static::$globalEvent->add(\SIGHUP, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall reload signal handler + static::$globalEvent->add(\SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful reload signal handler + static::$globalEvent->add(\SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall status signal handler + static::$globalEvent->add(\SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall connection status signal handler + static::$globalEvent->add(\SIGIO, EventInterface::EV_SIGNAL, $signalHandler); + } + + /** + * Signal handler. + * + * @param int $signal + */ + public static function signalHandler($signal) + { + switch ($signal) { + // Stop. + case \SIGINT: + case \SIGTERM: + static::$_gracefulStop = false; + static::stopAll(); + break; + // Graceful stop. + case \SIGHUP: + static::$_gracefulStop = true; + static::stopAll(); + break; + // Reload. + case \SIGQUIT: + case \SIGUSR1: + static::$_gracefulStop = $signal === \SIGQUIT; + static::$_pidsToRestart = static::getAllWorkerPids(); + static::reload(); + break; + // Show status. + case \SIGUSR2: + static::writeStatisticsToStatusFile(); + break; + // Show connection status. + case \SIGIO: + static::writeConnectionsStatisticsToStatusFile(); + break; + } + } + + /** + * Run as deamon mode. + * + * @throws Exception + */ + protected static function daemonize() + { + if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) { + return; + } + \umask(0); + $pid = \pcntl_fork(); + if (-1 === $pid) { + throw new Exception('Fork fail'); + } elseif ($pid > 0) { + exit(0); + } + if (-1 === \posix_setsid()) { + throw new Exception("Setsid fail"); + } + // Fork again avoid SVR4 system regain the control of terminal. + $pid = \pcntl_fork(); + if (-1 === $pid) { + throw new Exception("Fork fail"); + } elseif (0 !== $pid) { + exit(0); + } + } + + /** + * Redirect standard input and output. + * + * @throws Exception + */ + public static function resetStd() + { + if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) { + return; + } + global $STDOUT, $STDERR; + $handle = \fopen(static::$stdoutFile, "a"); + if ($handle) { + unset($handle); + \set_error_handler(function(){}); + if ($STDOUT) { + \fclose($STDOUT); + } + if ($STDERR) { + \fclose($STDERR); + } + \fclose(\STDOUT); + \fclose(\STDERR); + $STDOUT = \fopen(static::$stdoutFile, "a"); + $STDERR = \fopen(static::$stdoutFile, "a"); + // change output stream + static::$_outputStream = null; + static::outputStream($STDOUT); + \restore_error_handler(); + return; + } + + throw new Exception('Can not open stdoutFile ' . static::$stdoutFile); + } + + /** + * Save pid. + * + * @throws Exception + */ + protected static function saveMasterPid() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + + static::$_masterPid = \posix_getpid(); + if (false === \file_put_contents(static::$pidFile, static::$_masterPid)) { + throw new Exception('can not save pid to ' . static::$pidFile); + } + } + + /** + * Get event loop name. + * + * @return string + */ + protected static function getEventLoopName() + { + if (static::$eventLoopClass) { + return static::$eventLoopClass; + } + + if (!\class_exists('\Swoole\Event', false)) { + unset(static::$_availableEventLoops['swoole']); + } + + $loop_name = ''; + foreach (static::$_availableEventLoops as $name=>$class) { + if (\extension_loaded($name)) { + $loop_name = $name; + break; + } + } + + if ($loop_name) { + if (\interface_exists('\React\EventLoop\LoopInterface')) { + switch ($loop_name) { + case 'libevent': + static::$eventLoopClass = '\Workerman\Events\React\ExtLibEventLoop'; + break; + case 'event': + static::$eventLoopClass = '\Workerman\Events\React\ExtEventLoop'; + break; + default : + static::$eventLoopClass = '\Workerman\Events\React\StreamSelectLoop'; + break; + } + } else { + static::$eventLoopClass = static::$_availableEventLoops[$loop_name]; + } + } else { + static::$eventLoopClass = \interface_exists('\React\EventLoop\LoopInterface') ? '\Workerman\Events\React\StreamSelectLoop' : '\Workerman\Events\Select'; + } + return static::$eventLoopClass; + } + + /** + * Get all pids of worker processes. + * + * @return array + */ + protected static function getAllWorkerPids() + { + $pid_array = array(); + foreach (static::$_pidMap as $worker_pid_array) { + foreach ($worker_pid_array as $worker_pid) { + $pid_array[$worker_pid] = $worker_pid; + } + } + return $pid_array; + } + + /** + * Fork some worker processes. + * + * @return void + */ + protected static function forkWorkers() + { + if (static::$_OS === \OS_TYPE_LINUX) { + static::forkWorkersForLinux(); + } else { + static::forkWorkersForWindows(); + } + } + + /** + * Fork some worker processes. + * + * @return void + */ + protected static function forkWorkersForLinux() + { + + foreach (static::$_workers as $worker) { + if (static::$_status === static::STATUS_STARTING) { + if (empty($worker->name)) { + $worker->name = $worker->getSocketName(); + } + $worker_name_length = \strlen($worker->name); + if (static::$_maxWorkerNameLength < $worker_name_length) { + static::$_maxWorkerNameLength = $worker_name_length; + } + } + + while (\count(static::$_pidMap[$worker->workerId]) < $worker->count) { + static::forkOneWorkerForLinux($worker); + } + } + } + + /** + * Fork some worker processes. + * + * @return void + */ + protected static function forkWorkersForWindows() + { + $files = static::getStartFilesForWindows(); + global $argv; + if(\in_array('-q', $argv) || \count($files) === 1) + { + if(\count(static::$_workers) > 1) + { + static::safeEcho("@@@ Error: multi workers init in one php file are not support @@@\r\n"); + static::safeEcho("@@@ See http://doc.workerman.net/faq/multi-woker-for-windows.html @@@\r\n"); + } + elseif(\count(static::$_workers) <= 0) + { + exit("@@@no worker inited@@@\r\n\r\n"); + } + + \reset(static::$_workers); + /** @var Worker $worker */ + $worker = current(static::$_workers); + + // Display UI. + static::safeEcho(\str_pad($worker->name, 21) . \str_pad($worker->getSocketName(), 36) . \str_pad($worker->count, 10) . "[ok]\n"); + $worker->listen(); + $worker->run(); + exit("@@@child exit@@@\r\n"); + } + else + { + static::$globalEvent = new \Workerman\Events\Select(); + Timer::init(static::$globalEvent); + foreach($files as $start_file) + { + static::forkOneWorkerForWindows($start_file); + } + } + } + + /** + * Get start files for windows. + * + * @return array + */ + public static function getStartFilesForWindows() { + global $argv; + $files = array(); + foreach($argv as $file) + { + if(\is_file($file)) + { + $files[$file] = $file; + } + } + return $files; + } + + /** + * Fork one worker process. + * + * @param string $start_file + */ + public static function forkOneWorkerForWindows($start_file) + { + $start_file = \realpath($start_file); + $std_file = \sys_get_temp_dir() . '/'.\str_replace(array('/', "\\", ':'), '_', $start_file).'.out.txt'; + + $descriptorspec = array( + 0 => array('pipe', 'a'), // stdin + 1 => array('file', $std_file, 'w'), // stdout + 2 => array('file', $std_file, 'w') // stderr + ); + + + $pipes = array(); + $process = \proc_open("php \"$start_file\" -q", $descriptorspec, $pipes); + $std_handler = \fopen($std_file, 'a+'); + \stream_set_blocking($std_handler, false); + + if (empty(static::$globalEvent)) { + static::$globalEvent = new Select(); + Timer::init(static::$globalEvent); + } + $timer_id = Timer::add(0.1, function()use($std_handler) + { + Worker::safeEcho(\fread($std_handler, 65535)); + }); + + // 保存子进程句柄 + static::$_processForWindows[$start_file] = array($process, $start_file, $timer_id); + } + + /** + * check worker status for windows. + * @return void + */ + public static function checkWorkerStatusForWindows() + { + foreach(static::$_processForWindows as $process_data) + { + $process = $process_data[0]; + $start_file = $process_data[1]; + $timer_id = $process_data[2]; + $status = \proc_get_status($process); + if(isset($status['running'])) + { + if(!$status['running']) + { + static::safeEcho("process $start_file terminated and try to restart\n"); + Timer::del($timer_id); + \proc_close($process); + static::forkOneWorkerForWindows($start_file); + } + } + else + { + static::safeEcho("proc_get_status fail\n"); + } + } + } + + + /** + * Fork one worker process. + * + * @param self $worker + * @throws Exception + */ + protected static function forkOneWorkerForLinux(self $worker) + { + // Get available worker id. + $id = static::getId($worker->workerId, 0); + if ($id === false) { + return; + } + $pid = \pcntl_fork(); + // For master process. + if ($pid > 0) { + static::$_pidMap[$worker->workerId][$pid] = $pid; + static::$_idMap[$worker->workerId][$id] = $pid; + } // For child processes. + elseif (0 === $pid) { + \srand(); + \mt_srand(); + if ($worker->reusePort) { + $worker->listen(); + } + if (static::$_status === static::STATUS_STARTING) { + static::resetStd(); + } + static::$_pidMap = array(); + // Remove other listener. + foreach(static::$_workers as $key => $one_worker) { + if ($one_worker->workerId !== $worker->workerId) { + $one_worker->unlisten(); + unset(static::$_workers[$key]); + } + } + Timer::delAll(); + static::setProcessTitle(self::$processTitle . ': worker process ' . $worker->name . ' ' . $worker->getSocketName()); + $worker->setUserAndGroup(); + $worker->id = $id; + $worker->run(); + if (strpos(static::$eventLoopClass, 'Workerman\Events\Swoole') !== false) { + exit(0); + } + $err = new Exception('event-loop exited'); + static::log($err); + exit(250); + } else { + throw new Exception("forkOneWorker fail"); + } + } + + /** + * Get worker id. + * + * @param int $worker_id + * @param int $pid + * + * @return integer + */ + protected static function getId($worker_id, $pid) + { + return \array_search($pid, static::$_idMap[$worker_id]); + } + + /** + * Set unix user and group for current process. + * + * @return void + */ + public function setUserAndGroup() + { + // Get uid. + $user_info = \posix_getpwnam($this->user); + if (!$user_info) { + static::log("Warning: User {$this->user} not exsits"); + return; + } + $uid = $user_info['uid']; + // Get gid. + if ($this->group) { + $group_info = \posix_getgrnam($this->group); + if (!$group_info) { + static::log("Warning: Group {$this->group} not exsits"); + return; + } + $gid = $group_info['gid']; + } else { + $gid = $user_info['gid']; + } + + // Set uid and gid. + if ($uid !== \posix_getuid() || $gid !== \posix_getgid()) { + if (!\posix_setgid($gid) || !\posix_initgroups($user_info['name'], $gid) || !\posix_setuid($uid)) { + static::log("Warning: change gid or uid fail."); + } + } + } + + /** + * Set process name. + * + * @param string $title + * @return void + */ + protected static function setProcessTitle($title) + { + \set_error_handler(function(){}); + // >=php 5.5 + if (\function_exists('cli_set_process_title')) { + \cli_set_process_title($title); + } // Need proctitle when php<=5.5 . + elseif (\extension_loaded('proctitle') && \function_exists('setproctitle')) { + \setproctitle($title); + } + \restore_error_handler(); + } + + /** + * Monitor all child processes. + * + * @return void + */ + protected static function monitorWorkers() + { + if (static::$_OS === \OS_TYPE_LINUX) { + static::monitorWorkersForLinux(); + } else { + static::monitorWorkersForWindows(); + } + } + + /** + * Monitor all child processes. + * + * @return void + */ + protected static function monitorWorkersForLinux() + { + static::$_status = static::STATUS_RUNNING; + while (1) { + // Calls signal handlers for pending signals. + \pcntl_signal_dispatch(); + // Suspends execution of the current process until a child has exited, or until a signal is delivered + $status = 0; + $pid = \pcntl_wait($status, \WUNTRACED); + // Calls signal handlers for pending signals again. + \pcntl_signal_dispatch(); + // If a child has already exited. + if ($pid > 0) { + // Find out which worker process exited. + foreach (static::$_pidMap as $worker_id => $worker_pid_array) { + if (isset($worker_pid_array[$pid])) { + $worker = static::$_workers[$worker_id]; + // Exit status. + if ($status !== 0) { + static::log("worker[" . $worker->name . ":$pid] exit with status $status"); + } + + // For Statistics. + if (!isset(static::$_globalStatistics['worker_exit_info'][$worker_id][$status])) { + static::$_globalStatistics['worker_exit_info'][$worker_id][$status] = 0; + } + ++static::$_globalStatistics['worker_exit_info'][$worker_id][$status]; + + // Clear process data. + unset(static::$_pidMap[$worker_id][$pid]); + + // Mark id is available. + $id = static::getId($worker_id, $pid); + static::$_idMap[$worker_id][$id] = 0; + + break; + } + } + // Is still running state then fork a new worker process. + if (static::$_status !== static::STATUS_SHUTDOWN) { + static::forkWorkers(); + // If reloading continue. + if (isset(static::$_pidsToRestart[$pid])) { + unset(static::$_pidsToRestart[$pid]); + static::reload(); + } + } + } + + // If shutdown state and all child processes exited then master process exit. + if (static::$_status === static::STATUS_SHUTDOWN && !static::getAllWorkerPids()) { + static::exitAndClearAll(); + } + } + } + + /** + * Monitor all child processes. + * + * @return void + */ + protected static function monitorWorkersForWindows() + { + Timer::add(1, "\\Workerman\\Worker::checkWorkerStatusForWindows"); + + static::$globalEvent->loop(); + } + + /** + * Exit current process. + * + * @return void + */ + protected static function exitAndClearAll() + { + foreach (static::$_workers as $worker) { + $socket_name = $worker->getSocketName(); + if ($worker->transport === 'unix' && $socket_name) { + list(, $address) = \explode(':', $socket_name, 2); + @\unlink($address); + } + } + @\unlink(static::$pidFile); + static::log("Workerman[" . \basename(static::$_startFile) . "] has been stopped"); + if (static::$onMasterStop) { + \call_user_func(static::$onMasterStop); + } + exit(0); + } + + /** + * Execute reload. + * + * @return void + */ + protected static function reload() + { + // For master process. + if (static::$_masterPid === \posix_getpid()) { + // Set reloading state. + if (static::$_status !== static::STATUS_RELOADING && static::$_status !== static::STATUS_SHUTDOWN) { + static::log("Workerman[" . \basename(static::$_startFile) . "] reloading"); + static::$_status = static::STATUS_RELOADING; + // Try to emit onMasterReload callback. + if (static::$onMasterReload) { + try { + \call_user_func(static::$onMasterReload); + } catch (\Exception $e) { + static::log($e); + exit(250); + } catch (\Error $e) { + static::log($e); + exit(250); + } + static::initId(); + } + } + + if (static::$_gracefulStop) { + $sig = \SIGQUIT; + } else { + $sig = \SIGUSR1; + } + + // Send reload signal to all child processes. + $reloadable_pid_array = array(); + foreach (static::$_pidMap as $worker_id => $worker_pid_array) { + $worker = static::$_workers[$worker_id]; + if ($worker->reloadable) { + foreach ($worker_pid_array as $pid) { + $reloadable_pid_array[$pid] = $pid; + } + } else { + foreach ($worker_pid_array as $pid) { + // Send reload signal to a worker process which reloadable is false. + \posix_kill($pid, $sig); + } + } + } + + // Get all pids that are waiting reload. + static::$_pidsToRestart = \array_intersect(static::$_pidsToRestart, $reloadable_pid_array); + + // Reload complete. + if (empty(static::$_pidsToRestart)) { + if (static::$_status !== static::STATUS_SHUTDOWN) { + static::$_status = static::STATUS_RUNNING; + } + return; + } + // Continue reload. + $one_worker_pid = \current(static::$_pidsToRestart); + // Send reload signal to a worker process. + \posix_kill($one_worker_pid, $sig); + // If the process does not exit after static::KILL_WORKER_TIMER_TIME seconds try to kill it. + if(!static::$_gracefulStop){ + Timer::add(static::KILL_WORKER_TIMER_TIME, '\posix_kill', array($one_worker_pid, \SIGKILL), false); + } + } // For child processes. + else { + \reset(static::$_workers); + $worker = \current(static::$_workers); + // Try to emit onWorkerReload callback. + if ($worker->onWorkerReload) { + try { + \call_user_func($worker->onWorkerReload, $worker); + } catch (\Exception $e) { + static::log($e); + exit(250); + } catch (\Error $e) { + static::log($e); + exit(250); + } + } + + if ($worker->reloadable) { + static::stopAll(); + } + } + } + + /** + * Stop. + * + * @return void + */ + public static function stopAll() + { + static::$_status = static::STATUS_SHUTDOWN; + // For master process. + if (static::$_masterPid === \posix_getpid()) { + static::log("Workerman[" . \basename(static::$_startFile) . "] stopping ..."); + $worker_pid_array = static::getAllWorkerPids(); + // Send stop signal to all child processes. + if (static::$_gracefulStop) { + $sig = \SIGHUP; + } else { + $sig = \SIGINT; + } + foreach ($worker_pid_array as $worker_pid) { + \posix_kill($worker_pid, $sig); + if(!static::$_gracefulStop){ + Timer::add(static::KILL_WORKER_TIMER_TIME, '\posix_kill', array($worker_pid, \SIGKILL), false); + } + } + Timer::add(1, "\\Workerman\\Worker::checkIfChildRunning"); + // Remove statistics file. + if (\is_file(static::$_statisticsFile)) { + @\unlink(static::$_statisticsFile); + } + } // For child processes. + else { + // Execute exit. + foreach (static::$_workers as $worker) { + if(!$worker->stopping){ + $worker->stop(); + $worker->stopping = true; + } + } + if (!static::$_gracefulStop || ConnectionInterface::$statistics['connection_count'] <= 0) { + static::$_workers = array(); + if (static::$globalEvent) { + static::$globalEvent->destroy(); + } + + try { + exit(0); + } catch (Exception $e) { + + } + } + } + } + + /** + * check if child processes is really running + */ + public static function checkIfChildRunning() + { + foreach (static::$_pidMap as $worker_id => $worker_pid_array) { + foreach ($worker_pid_array as $pid => $worker_pid) { + if (!\posix_kill($pid, 0)) { + unset(static::$_pidMap[$worker_id][$pid]); + } + } + } + } + + /** + * Get process status. + * + * @return number + */ + public static function getStatus() + { + return static::$_status; + } + + /** + * If stop gracefully. + * + * @return bool + */ + public static function getGracefulStop() + { + return static::$_gracefulStop; + } + + /** + * Write statistics data to disk. + * + * @return void + */ + protected static function writeStatisticsToStatusFile() + { + // For master process. + if (static::$_masterPid === \posix_getpid()) { + $all_worker_info = array(); + foreach(static::$_pidMap as $worker_id => $pid_array) { + /** @var /Workerman/Worker $worker */ + $worker = static::$_workers[$worker_id]; + foreach($pid_array as $pid) { + $all_worker_info[$pid] = array('name' => $worker->name, 'listen' => $worker->getSocketName()); + } + } + + \file_put_contents(static::$_statisticsFile, \serialize($all_worker_info)."\n", \FILE_APPEND); + $loadavg = \function_exists('sys_getloadavg') ? \array_map('round', \sys_getloadavg(), array(2)) : array('-', '-', '-'); + \file_put_contents(static::$_statisticsFile, + "----------------------------------------------GLOBAL STATUS----------------------------------------------------\n", \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, + 'Workerman version:' . static::VERSION . " PHP version:" . \PHP_VERSION . "\n", \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, 'start time:' . \date('Y-m-d H:i:s', + static::$_globalStatistics['start_timestamp']) . ' run ' . \floor((\time() - static::$_globalStatistics['start_timestamp']) / (24 * 60 * 60)) . ' days ' . \floor(((\time() - static::$_globalStatistics['start_timestamp']) % (24 * 60 * 60)) / (60 * 60)) . " hours \n", + FILE_APPEND); + $load_str = 'load average: ' . \implode(", ", $loadavg); + \file_put_contents(static::$_statisticsFile, + \str_pad($load_str, 33) . 'event-loop:' . static::getEventLoopName() . "\n", \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, + \count(static::$_pidMap) . ' workers ' . \count(static::getAllWorkerPids()) . " processes\n", + \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, + \str_pad('worker_name', static::$_maxWorkerNameLength) . " exit_status exit_count\n", \FILE_APPEND); + foreach (static::$_pidMap as $worker_id => $worker_pid_array) { + $worker = static::$_workers[$worker_id]; + if (isset(static::$_globalStatistics['worker_exit_info'][$worker_id])) { + foreach (static::$_globalStatistics['worker_exit_info'][$worker_id] as $worker_exit_status => $worker_exit_count) { + \file_put_contents(static::$_statisticsFile, + \str_pad($worker->name, static::$_maxWorkerNameLength) . " " . \str_pad($worker_exit_status, + 16) . " $worker_exit_count\n", \FILE_APPEND); + } + } else { + \file_put_contents(static::$_statisticsFile, + \str_pad($worker->name, static::$_maxWorkerNameLength) . " " . \str_pad(0, 16) . " 0\n", + \FILE_APPEND); + } + } + \file_put_contents(static::$_statisticsFile, + "----------------------------------------------PROCESS STATUS---------------------------------------------------\n", + \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, + "pid\tmemory " . \str_pad('listening', static::$_maxSocketNameLength) . " " . \str_pad('worker_name', + static::$_maxWorkerNameLength) . " connections " . \str_pad('send_fail', 9) . " " + . \str_pad('timers', 8) . \str_pad('total_request', 13) ." qps status\n", \FILE_APPEND); + + \chmod(static::$_statisticsFile, 0722); + + foreach (static::getAllWorkerPids() as $worker_pid) { + \posix_kill($worker_pid, \SIGUSR2); + } + return; + } + + // For child processes. + \reset(static::$_workers); + /** @var \Workerman\Worker $worker */ + $worker = current(static::$_workers); + $worker_status_str = \posix_getpid() . "\t" . \str_pad(round(memory_get_usage(true) / (1024 * 1024), 2) . "M", 7) + . " " . \str_pad($worker->getSocketName(), static::$_maxSocketNameLength) . " " + . \str_pad(($worker->name === $worker->getSocketName() ? 'none' : $worker->name), static::$_maxWorkerNameLength) + . " "; + $worker_status_str .= \str_pad(ConnectionInterface::$statistics['connection_count'], 11) + . " " . \str_pad(ConnectionInterface::$statistics['send_fail'], 9) + . " " . \str_pad(static::$globalEvent->getTimerCount(), 7) + . " " . \str_pad(ConnectionInterface::$statistics['total_request'], 13) . "\n"; + \file_put_contents(static::$_statisticsFile, $worker_status_str, \FILE_APPEND); + } + + /** + * Write statistics data to disk. + * + * @return void + */ + protected static function writeConnectionsStatisticsToStatusFile() + { + // For master process. + if (static::$_masterPid === \posix_getpid()) { + \file_put_contents(static::$_statisticsFile, "--------------------------------------------------------------------- WORKERMAN CONNECTION STATUS --------------------------------------------------------------------------------\n", \FILE_APPEND); + \file_put_contents(static::$_statisticsFile, "PID Worker CID Trans Protocol ipv4 ipv6 Recv-Q Send-Q Bytes-R Bytes-W Status Local Address Foreign Address\n", \FILE_APPEND); + \chmod(static::$_statisticsFile, 0722); + foreach (static::getAllWorkerPids() as $worker_pid) { + \posix_kill($worker_pid, \SIGIO); + } + return; + } + + // For child processes. + $bytes_format = function($bytes) + { + if($bytes > 1024*1024*1024*1024) { + return round($bytes/(1024*1024*1024*1024), 1)."TB"; + } + if($bytes > 1024*1024*1024) { + return round($bytes/(1024*1024*1024), 1)."GB"; + } + if($bytes > 1024*1024) { + return round($bytes/(1024*1024), 1)."MB"; + } + if($bytes > 1024) { + return round($bytes/(1024), 1)."KB"; + } + return $bytes."B"; + }; + + $pid = \posix_getpid(); + $str = ''; + \reset(static::$_workers); + $current_worker = current(static::$_workers); + $default_worker_name = $current_worker->name; + + /** @var \Workerman\Worker $worker */ + foreach(TcpConnection::$connections as $connection) { + /** @var \Workerman\Connection\TcpConnection $connection */ + $transport = $connection->transport; + $ipv4 = $connection->isIpV4() ? ' 1' : ' 0'; + $ipv6 = $connection->isIpV6() ? ' 1' : ' 0'; + $recv_q = $bytes_format($connection->getRecvBufferQueueSize()); + $send_q = $bytes_format($connection->getSendBufferQueueSize()); + $local_address = \trim($connection->getLocalAddress()); + $remote_address = \trim($connection->getRemoteAddress()); + $state = $connection->getStatus(false); + $bytes_read = $bytes_format($connection->bytesRead); + $bytes_written = $bytes_format($connection->bytesWritten); + $id = $connection->id; + $protocol = $connection->protocol ? $connection->protocol : $connection->transport; + $pos = \strrpos($protocol, '\\'); + if ($pos) { + $protocol = \substr($protocol, $pos+1); + } + if (\strlen($protocol) > 15) { + $protocol = \substr($protocol, 0, 13) . '..'; + } + $worker_name = isset($connection->worker) ? $connection->worker->name : $default_worker_name; + if (\strlen($worker_name) > 14) { + $worker_name = \substr($worker_name, 0, 12) . '..'; + } + $str .= \str_pad($pid, 9) . \str_pad($worker_name, 16) . \str_pad($id, 10) . \str_pad($transport, 8) + . \str_pad($protocol, 16) . \str_pad($ipv4, 7) . \str_pad($ipv6, 7) . \str_pad($recv_q, 13) + . \str_pad($send_q, 13) . \str_pad($bytes_read, 13) . \str_pad($bytes_written, 13) . ' ' + . \str_pad($state, 14) . ' ' . \str_pad($local_address, 22) . ' ' . \str_pad($remote_address, 22) ."\n"; + } + if ($str) { + \file_put_contents(static::$_statisticsFile, $str, \FILE_APPEND); + } + } + + /** + * Check errors when current process exited. + * + * @return void + */ + public static function checkErrors() + { + if (static::STATUS_SHUTDOWN !== static::$_status) { + $error_msg = static::$_OS === \OS_TYPE_LINUX ? 'Worker['. \posix_getpid() .'] process terminated' : 'Worker process terminated'; + $errors = error_get_last(); + if ($errors && ($errors['type'] === \E_ERROR || + $errors['type'] === \E_PARSE || + $errors['type'] === \E_CORE_ERROR || + $errors['type'] === \E_COMPILE_ERROR || + $errors['type'] === \E_RECOVERABLE_ERROR) + ) { + $error_msg .= ' with ERROR: ' . static::getErrorType($errors['type']) . " \"{$errors['message']} in {$errors['file']} on line {$errors['line']}\""; + } + static::log($error_msg); + } + } + + /** + * Get error message by error code. + * + * @param integer $type + * @return string + */ + protected static function getErrorType($type) + { + if(isset(self::$_errorType[$type])) { + return self::$_errorType[$type]; + } + + return ''; + } + + /** + * Log. + * + * @param string $msg + * @return void + */ + public static function log($msg) + { + $msg = $msg . "\n"; + if (!static::$daemonize) { + static::safeEcho($msg); + } + \file_put_contents((string)static::$logFile, \date('Y-m-d H:i:s') . ' ' . 'pid:' + . (static::$_OS === \OS_TYPE_LINUX ? \posix_getpid() : 1) . ' ' . $msg, \FILE_APPEND | \LOCK_EX); + } + + /** + * Safe Echo. + * @param string $msg + * @param bool $decorated + * @return bool + */ + public static function safeEcho($msg, $decorated = false) + { + $stream = static::outputStream(); + if (!$stream) { + return false; + } + if (!$decorated) { + $line = $white = $green = $end = ''; + if (static::$_outputDecorated) { + $line = "\033[1A\n\033[K"; + $white = "\033[47;30m"; + $green = "\033[32;40m"; + $end = "\033[0m"; + } + $msg = \str_replace(array('', '', ''), array($line, $white, $green), $msg); + $msg = \str_replace(array('', '', ''), $end, $msg); + } elseif (!static::$_outputDecorated) { + return false; + } + \fwrite($stream, $msg); + \fflush($stream); + return true; + } + + /** + * @param null $stream + * @return bool|resource + */ + private static function outputStream($stream = null) + { + if (!$stream) { + $stream = static::$_outputStream ? static::$_outputStream : \STDOUT; + } + if (!$stream || !\is_resource($stream) || 'stream' !== \get_resource_type($stream)) { + return false; + } + $stat = \fstat($stream); + if (!$stat) { + return false; + } + if (($stat['mode'] & 0170000) === 0100000) { + // file + static::$_outputDecorated = false; + } else { + static::$_outputDecorated = + static::$_OS === \OS_TYPE_LINUX && + \function_exists('posix_isatty') && + \posix_isatty($stream); + } + return static::$_outputStream = $stream; + } + + /** + * Construct. + * + * @param string $socket_name + * @param array $context_option + */ + public function __construct($socket_name = '', array $context_option = array()) + { + // Save all worker instances. + $this->workerId = \spl_object_hash($this); + static::$_workers[$this->workerId] = $this; + static::$_pidMap[$this->workerId] = array(); + + // Get autoload root path. + $backtrace = \debug_backtrace(); + $this->_autoloadRootPath = \dirname($backtrace[0]['file']); + Autoloader::setRootPath($this->_autoloadRootPath); + + // Context for socket. + if ($socket_name) { + $this->_socketName = $socket_name; + if (!isset($context_option['socket']['backlog'])) { + $context_option['socket']['backlog'] = static::DEFAULT_BACKLOG; + } + $this->_context = \stream_context_create($context_option); + } + + // Turn reusePort on. + if (static::$_OS === \OS_TYPE_LINUX // if linux + && \version_compare(\PHP_VERSION,'7.0.0', 'ge') // if php >= 7.0.0 + && \strtolower(\php_uname('s')) !== 'darwin' // if not Mac OS + && strpos($socket_name,'unix') !== 0) { // if not unix socket + + $this->reusePort = true; + } + } + + + /** + * Listen. + * + * @throws Exception + */ + public function listen() + { + if (!$this->_socketName) { + return; + } + + // Autoload. + Autoloader::setRootPath($this->_autoloadRootPath); + + if (!$this->_mainSocket) { + + $local_socket = $this->parseSocketAddress(); + + // Flag. + $flags = $this->transport === 'udp' ? \STREAM_SERVER_BIND : \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN; + $errno = 0; + $errmsg = ''; + // SO_REUSEPORT. + if ($this->reusePort) { + \stream_context_set_option($this->_context, 'socket', 'so_reuseport', 1); + } + + // Create an Internet or Unix domain server socket. + $this->_mainSocket = \stream_socket_server($local_socket, $errno, $errmsg, $flags, $this->_context); + if (!$this->_mainSocket) { + throw new Exception($errmsg); + } + + if ($this->transport === 'ssl') { + \stream_socket_enable_crypto($this->_mainSocket, false); + } elseif ($this->transport === 'unix') { + $socket_file = \substr($local_socket, 7); + if ($this->user) { + \chown($socket_file, $this->user); + } + if ($this->group) { + \chgrp($socket_file, $this->group); + } + } + + // Try to open keepalive for tcp and disable Nagle algorithm. + if (\function_exists('socket_import_stream') && static::$_builtinTransports[$this->transport] === 'tcp') { + \set_error_handler(function(){}); + $socket = \socket_import_stream($this->_mainSocket); + \socket_set_option($socket, \SOL_SOCKET, \SO_KEEPALIVE, 1); + \socket_set_option($socket, \SOL_TCP, \TCP_NODELAY, 1); + \restore_error_handler(); + } + + // Non blocking. + \stream_set_blocking($this->_mainSocket, false); + } + + $this->resumeAccept(); + } + + /** + * Unlisten. + * + * @return void + */ + public function unlisten() { + $this->pauseAccept(); + if ($this->_mainSocket) { + \set_error_handler(function(){}); + \fclose($this->_mainSocket); + \restore_error_handler(); + $this->_mainSocket = null; + } + } + + /** + * Parse local socket address. + * + * @throws Exception + */ + protected function parseSocketAddress() { + if (!$this->_socketName) { + return; + } + // Get the application layer communication protocol and listening address. + list($scheme, $address) = \explode(':', $this->_socketName, 2); + // Check application layer protocol class. + if (!isset(static::$_builtinTransports[$scheme])) { + $scheme = \ucfirst($scheme); + $this->protocol = \substr($scheme,0,1)==='\\' ? $scheme : 'Protocols\\' . $scheme; + if (!\class_exists($this->protocol)) { + $this->protocol = "Workerman\\Protocols\\$scheme"; + if (!\class_exists($this->protocol)) { + throw new Exception("class \\Protocols\\$scheme not exist"); + } + } + + if (!isset(static::$_builtinTransports[$this->transport])) { + throw new Exception('Bad worker->transport ' . \var_export($this->transport, true)); + } + } else { + $this->transport = $scheme; + } + //local socket + return static::$_builtinTransports[$this->transport] . ":" . $address; + } + + /** + * Pause accept new connections. + * + * @return void + */ + public function pauseAccept() + { + if (static::$globalEvent && false === $this->_pauseAccept && $this->_mainSocket) { + static::$globalEvent->del($this->_mainSocket, EventInterface::EV_READ); + $this->_pauseAccept = true; + } + } + + /** + * Resume accept new connections. + * + * @return void + */ + public function resumeAccept() + { + // Register a listener to be notified when server socket is ready to read. + if (static::$globalEvent && true === $this->_pauseAccept && $this->_mainSocket) { + if ($this->transport !== 'udp') { + static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection')); + } else { + static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection')); + } + $this->_pauseAccept = false; + } + } + + /** + * Get socket name. + * + * @return string + */ + public function getSocketName() + { + return $this->_socketName ? \lcfirst($this->_socketName) : 'none'; + } + + /** + * Run worker instance. + * + * @return void + */ + public function run() + { + //Update process state. + static::$_status = static::STATUS_RUNNING; + + // Register shutdown function for checking errors. + \register_shutdown_function(array("\\Workerman\\Worker", 'checkErrors')); + + // Set autoload root path. + Autoloader::setRootPath($this->_autoloadRootPath); + + // Create a global event loop. + if (!static::$globalEvent) { + $event_loop_class = static::getEventLoopName(); + static::$globalEvent = new $event_loop_class; + $this->resumeAccept(); + } + + // Reinstall signal. + static::reinstallSignal(); + + // Init Timer. + Timer::init(static::$globalEvent); + + // Set an empty onMessage callback. + if (empty($this->onMessage)) { + $this->onMessage = function () {}; + } + + \restore_error_handler(); + + // Try to emit onWorkerStart callback. + if ($this->onWorkerStart) { + try { + \call_user_func($this->onWorkerStart, $this); + } catch (\Exception $e) { + static::log($e); + // Avoid rapid infinite loop exit. + sleep(1); + exit(250); + } catch (\Error $e) { + static::log($e); + // Avoid rapid infinite loop exit. + sleep(1); + exit(250); + } + } + + // Main loop. + static::$globalEvent->loop(); + } + + /** + * Stop current worker instance. + * + * @return void + */ + public function stop() + { + // Try to emit onWorkerStop callback. + if ($this->onWorkerStop) { + try { + \call_user_func($this->onWorkerStop, $this); + } catch (\Exception $e) { + static::log($e); + exit(250); + } catch (\Error $e) { + static::log($e); + exit(250); + } + } + // Remove listener for server socket. + $this->unlisten(); + // Close all connections for the worker. + if (!static::$_gracefulStop) { + foreach ($this->connections as $connection) { + $connection->close(); + } + } + // Clear callback. + $this->onMessage = $this->onClose = $this->onError = $this->onBufferDrain = $this->onBufferFull = null; + } + + /** + * Accept a connection. + * + * @param resource $socket + * @return void + */ + public function acceptConnection($socket) + { + // Accept a connection on server socket. + \set_error_handler(function(){}); + $new_socket = \stream_socket_accept($socket, 0, $remote_address); + \restore_error_handler(); + + // Thundering herd. + if (!$new_socket) { + return; + } + + // TcpConnection. + $connection = new TcpConnection($new_socket, $remote_address); + $this->connections[$connection->id] = $connection; + $connection->worker = $this; + $connection->protocol = $this->protocol; + $connection->transport = $this->transport; + $connection->onMessage = $this->onMessage; + $connection->onClose = $this->onClose; + $connection->onError = $this->onError; + $connection->onBufferDrain = $this->onBufferDrain; + $connection->onBufferFull = $this->onBufferFull; + + // Try to emit onConnect callback. + if ($this->onConnect) { + try { + \call_user_func($this->onConnect, $connection); + } catch (\Exception $e) { + static::log($e); + exit(250); + } catch (\Error $e) { + static::log($e); + exit(250); + } + } + } + + /** + * For udp package. + * + * @param resource $socket + * @return bool + */ + public function acceptUdpConnection($socket) + { + \set_error_handler(function(){}); + $recv_buffer = \stream_socket_recvfrom($socket, static::MAX_UDP_PACKAGE_SIZE, 0, $remote_address); + \restore_error_handler(); + if (false === $recv_buffer || empty($remote_address)) { + return false; + } + // UdpConnection. + $connection = new UdpConnection($socket, $remote_address); + $connection->protocol = $this->protocol; + if ($this->onMessage) { + try { + if ($this->protocol !== null) { + /** @var \Workerman\Protocols\ProtocolInterface $parser */ + $parser = $this->protocol; + if ($parser && \method_exists($parser, 'input')) { + while ($recv_buffer !== '') { + $len = $parser::input($recv_buffer, $connection); + if ($len === 0) + return true; + $package = \substr($recv_buffer, 0, $len); + $recv_buffer = \substr($recv_buffer, $len); + $data = $parser::decode($package, $connection); + if ($data === false) + continue; + \call_user_func($this->onMessage, $connection, $data); + } + } else { + $data = $parser::decode($recv_buffer, $connection); + // Discard bad packets. + if ($data === false) + return true; + \call_user_func($this->onMessage, $connection, $data); + } + } else { + \call_user_func($this->onMessage, $connection, $recv_buffer); + } + ++ConnectionInterface::$statistics['total_request']; + } catch (\Exception $e) { + static::log($e); + exit(250); + } catch (\Error $e) { + static::log($e); + exit(250); + } + } + return true; + } +} diff --git a/addons/weliam_smartcity/vendor/workerman/workerman/composer.json b/addons/weliam_smartcity/vendor/workerman/workerman/composer.json new file mode 100644 index 0000000..fdd4808 --- /dev/null +++ b/addons/weliam_smartcity/vendor/workerman/workerman/composer.json @@ -0,0 +1,38 @@ +{ + "name": "workerman/workerman", + "type": "library", + "keywords": [ + "event-loop", + "asynchronous" + ], + "homepage": "http://www.workerman.net", + "license": "MIT", + "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "support": { + "email": "walkor@workerman.net", + "issues": "https://github.com/walkor/workerman/issues", + "forum": "http://wenda.workerman.net/", + "wiki": "http://doc.workerman.net/", + "source": "https://github.com/walkor/workerman" + }, + "require": { + "php": ">=5.3" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "autoload": { + "psr-4": { + "Workerman\\": "./" + } + }, + "minimum-stability": "dev" +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/bug-report.md b/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..a4fc9ad --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,23 @@ +--- +name: Bug Report +about: Bug report + +--- + +## 包版本号 + + +## 问题描述 + + +## 你的代码 + + +## 报错详情 + + +## sdk 日志 + + +## nginx/apache 日志 +> 涉及到 异步通知、同步通知 的问题,请贴出来 diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/feature_request.md b/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..066b2d9 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/.gitignore b/addons/weliam_smartcity/vendor/yansongda/pay/.gitignore new file mode 100644 index 0000000..6de499c --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/.gitignore @@ -0,0 +1,4 @@ +/vendor +composer.lock +*.DS_Store +.idea diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/LICENSE b/addons/weliam_smartcity/vendor/yansongda/pay/LICENSE new file mode 100644 index 0000000..6ceb153 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 yansongda + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/README.md b/addons/weliam_smartcity/vendor/yansongda/pay/README.md new file mode 100644 index 0000000..91334c5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/README.md @@ -0,0 +1,307 @@ +

Pay

+ +

+StyleCI +Scrutinizer Code Quality +Build Status +Latest Stable Version +Total Downloads +Latest Unstable Version +License +

+ +该文档为 v2.x 版本,如果您想找 v1.x 版本文档,请点击[https://github.com/yansongda/pay/tree/v1.x](https://github.com/yansongda/pay/tree/v1.x) + +**注意:v1.x 与 v2.x 版本不兼容** + + +开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。 + +**!!请先熟悉 支付宝/微信 说明文档!!请具有基本的 debug 能力!!** + +欢迎 Star,欢迎 PR! + +laravel 扩展包请 [传送至这里](https://github.com/yansongda/laravel-pay) + +QQ交流群:690027516 + +## 特点 +- 丰富的事件系统 +- 命名不那么乱七八糟 +- 隐藏开发者不需要关注的细节 +- 根据支付宝、微信最新 API 开发而成 +- 高度抽象的类,免去各种拼json与xml的痛苦 +- 符合 PSR 标准,你可以各种方便的与你的框架集成 +- 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关 +- 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的 + + +## 运行环境 +- PHP 7.0+ (v2.8.0 开始 >= 7.1.3) +- composer + +> php5 请使用 v1.x 版本[https://github.com/yansongda/pay/tree/v1.x](https://github.com/yansongda/pay/tree/v1.x) + + +## 支持的支付方法 +### 1、支付宝 +- 电脑支付 +- 手机网站支付 +- APP 支付 +- 刷卡支付 +- 扫码支付 +- 账户转账 +- 小程序支付 + +| method | 描述 | +| :-------: | :-------: | +| web | 电脑支付 | +| wap | 手机网站支付 | +| app | APP 支付 | +| pos | 刷卡支付 | +| scan | 扫码支付 | +| transfer | 帐户转账 | +| mini | 小程序支付 | + +### 2、微信 +- 公众号支付 +- 小程序支付 +- H5 支付 +- 扫码支付 +- 刷卡支付 +- APP 支付 +- 企业付款 +- 普通红包 +- 分裂红包 + +| method | 描述 | +| :-----: | :-------: | +| mp | 公众号支付 | +| miniapp | 小程序支付 | +| wap | H5 支付 | +| scan | 扫码支付 | +| pos | 刷卡支付 | +| app | APP 支付 | +| transfer | 企业付款 | +| redpack | 普通红包 | +| groupRedpack | 分裂红包 | + +## 支持的方法 +所有网关均支持以下方法 + +- find(array/string $order) +说明:查找订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:查询成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- refund(array $order) +说明:退款接口 +参数:`$order` 数组格式,退款参数。 +返回:退款成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- cancel(array/string $order) +说明:取消订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:取消成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- close(array/string $order) +说明:关闭订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:关闭成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- verify() +说明:验证服务器返回消息是否合法 +返回:验证成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- PAYMETHOD(array $order) +说明:进行支付;具体支付方法名称请参考「支持的支付方法」一栏 +返回:成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据或 `Symfony\Component\HttpFoundation\Response` 实例,可通过 `return $response->send()`(laravel 框架中直接 `return $response`) 返回,具体请参考文档。 +异常:`GatewayException` 或 `InvalidSignException` + +## 安装 +```shell +composer require yansongda/pay -vvv +``` + +## 使用说明 + +### 支付宝 +```php + '2016082000295641', + 'notify_url' => 'http://yansongda.cn/notify.php', + 'return_url' => 'http://yansongda.cn/return.php', + 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB', + // 加密方式: **RSA2** + 'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==', + 'log' => [ // optional + 'file' => './logs/alipay.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + 'mode' => 'dev', // optional,设置此参数,将进入沙箱模式 + ]; + + public function index() + { + $order = [ + 'out_trade_no' => time(), + 'total_amount' => '1', + 'subject' => 'test subject - 测试', + ]; + + $alipay = Pay::alipay($this->config)->web($order); + + return $alipay->send();// laravel 框架中请直接 `return $alipay` + } + + public function return() + { + $data = Pay::alipay($this->config)->verify(); // 是的,验签就这么简单! + + // 订单号:$data->out_trade_no + // 支付宝交易号:$data->trade_no + // 订单总金额:$data->total_amount + } + + public function notify() + { + $alipay = Pay::alipay($this->config); + + try{ + $data = $alipay->verify(); // 是的,验签就这么简单! + + // 请自行对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。 + // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号; + // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); + // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email); + // 4、验证app_id是否为该商户本身。 + // 5、其它业务逻辑情况 + + Log::debug('Alipay notify', $data->all()); + } catch (\Exception $e) { + // $e->getMessage(); + } + + return $alipay->success()->send();// laravel 框架中请直接 `return $alipay->success()` + } +} +``` + +### 微信 +```php + 'wxb3fxxxxxxxxxxx', // APP APPID + 'app_id' => 'wxb3fxxxxxxxxxxx', // 公众号 APPID + 'miniapp_id' => 'wxb3fxxxxxxxxxxx', // 小程序 APPID + 'mch_id' => '14577xxxx', + 'key' => 'mF2suE9sU6Mk1Cxxxxxxxxxxx', + 'notify_url' => 'http://yanda.net.cn/notify.php', + 'cert_client' => './cert/apiclient_cert.pem', // optional,退款等情况时用到 + 'cert_key' => './cert/apiclient_key.pem',// optional,退款等情况时用到 + 'log' => [ // optional + 'file' => './logs/wechat.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + 'mode' => 'dev', // optional, dev/hk;当为 `hk` 时,为香港 gateway。 + ]; + + public function index() + { + $order = [ + 'out_trade_no' => time(), + 'total_fee' => '1', // **单位:分** + 'body' => 'test body - 测试', + 'openid' => 'onkVf1FjWS5SBIixxxxxxx', + ]; + + $pay = Pay::wechat($this->config)->mp($order); + + // $pay->appId + // $pay->timeStamp + // $pay->nonceStr + // $pay->package + // $pay->signType + } + + public function notify() + { + $pay = Pay::wechat($this->config); + + try{ + $data = $pay->verify(); // 是的,验签就这么简单! + + Log::debug('Wechat notify', $data->all()); + } catch (\Exception $e) { + // $e->getMessage(); + } + + return $pay->success()->send();// laravel 框架中请直接 `return $pay->success()` + } +} +``` + +## 事件系统 +[请见详细文档](http://pay.yansongda.cn) + +## 详细文档 +[详细说明文档](http://pay.yansongda.cn) + +## 错误 +如果在调用相关支付网关 API 时有错误产生,会抛出 `GatewayException`,`InvalidSignException` 错误,可以通过 `$e->getMessage()` 查看,同时,也可通过 `$e->raw` 查看调用 API 后返回的原始数据,该值为数组格式。 + +### 所有异常 + +* Yansongda\Pay\Exceptions\InvalidGatewayException ,表示使用了除本 SDK 支持的支付网关。 +* Yansongda\Pay\Exceptions\InvalidSignException ,表示验签失败。 +* Yansongda\Pay\Exceptions\InvalidConfigException ,表示缺少配置参数,如,`ali_public_key`, `private_key` 等。 +* Yansongda\Pay\Exceptions\GatewayException ,表示支付宝/微信服务器返回的数据非正常结果,例如,参数错误,对账单不存在等。 + + +## 代码贡献 +由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 + +如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_** + +## 赏一杯咖啡吧 + +![pay](https://pay.yanda.net.cn/images/pay.jpg) + +## LICENSE +MIT diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/composer.json b/addons/weliam_smartcity/vendor/yansongda/pay/composer.json new file mode 100644 index 0000000..f61faac --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/composer.json @@ -0,0 +1,41 @@ +{ + "name": "yansongda/pay", + "description": "专注 Alipay 和 WeChat 的支付扩展包", + "keywords": ["alipay", "wechat", "pay"], + "type": "library", + "support": { + "issues": "https://github.com/yansongda/pay/issues", + "source": "https://github.com/yansongda/pay" + }, + "authors": [ + { + "name": "yansongda", + "email": "me@yansongda.cn" + } + ], + "require": { + "php": ">=7.1.3", + "ext-openssl": "*", + "ext-simplexml":"*", + "ext-libxml": "*", + "ext-json": "*", + "yansongda/supports": "^1.8", + "symfony/http-foundation": "^4.0", + "symfony/event-dispatcher": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5", + "mockery/mockery": "^1.2" + }, + "autoload": { + "psr-4": { + "Yansongda\\Pay\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Yansongda\\Pay\\Tests\\": "tests" + } + }, + "license": "MIT" +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/phpunit.xml.dist b/addons/weliam_smartcity/vendor/yansongda/pay/phpunit.xml.dist new file mode 100644 index 0000000..e47284c --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + ./tests/ + + + + + src/ + + + diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayApplicationInterface.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayApplicationInterface.php new file mode 100644 index 0000000..72cd480 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayApplicationInterface.php @@ -0,0 +1,87 @@ + + * + * @param string $gateway + * @param array $params + * + * @return Collection|Response + */ + public function pay($gateway, $params); + + /** + * Query an order. + * + * @author yansongda + * + * @param string|array $order + * @param string $type + * + * @return Collection + */ + public function find($order, string $type); + + /** + * Refund an order. + * + * @author yansongda + * + * @param array $order + * + * @return Collection + */ + public function refund(array $order); + + /** + * Cancel an order. + * + * @author yansongda + * + * @param string|array $order + * + * @return Collection + */ + public function cancel($order); + + /** + * Close an order. + * + * @author yansongda + * + * @param string|array $order + * + * @return Collection + */ + public function close($order); + + /** + * Verify a request. + * + * @author yansongda + * + * @param string|array|null $content + * @param bool $refund + * + * @return Collection + */ + public function verify($content, bool $refund); + + /** + * Echo success to server. + * + * @author yansongda + * + * @return Response + */ + public function success(); +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayInterface.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayInterface.php new file mode 100644 index 0000000..fb165ab --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Contracts/GatewayInterface.php @@ -0,0 +1,21 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @return Collection|Response + */ + public function pay($endpoint, array $payload); +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events.php new file mode 100644 index 0000000..e6f6dec --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events.php @@ -0,0 +1,106 @@ + + * + * @method static Event dispatch(Event $event) Dispatches an event to all registered listeners + * @method static array getListeners($eventName = null) Gets the listeners of a specific event or all listeners sorted by descending priority. + * @method static int|void getListenerPriority($eventName, $listener) Gets the listener priority for a specific event. + * @method static bool hasListeners($eventName = null) Checks whether an event has any registered listeners. + * @method static void addListener($eventName, $listener, $priority = 0) Adds an event listener that listens on the specified events. + * @method static removeListener($eventName, $listener) Removes an event listener from the specified events. + * @method static void addSubscriber(EventSubscriberInterface $subscriber) Adds an event subscriber. + * @method static void removeSubscriber(EventSubscriberInterface $subscriber) + */ +class Events +{ + /** + * dispatcher. + * + * @var EventDispatcher + */ + protected static $dispatcher; + + /** + * Forward call. + * + * @author yansongda + * + * @param string $method + * @param array $args + * + * @throws Exception + * + * @return mixed + */ + public static function __callStatic($method, $args) + { + return call_user_func_array([self::getDispatcher(), $method], $args); + } + + /** + * Forward call. + * + * @author yansongda + * + * @param string $method + * @param array $args + * + * @throws Exception + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array([self::getDispatcher(), $method], $args); + } + + /** + * setDispatcher. + * + * @author yansongda + * + * @param EventDispatcher $dispatcher + * + * @return void + */ + public static function setDispatcher(EventDispatcher $dispatcher) + { + self::$dispatcher = $dispatcher; + } + + /** + * getDispatcher. + * + * @author yansongda + * + * @return EventDispatcher + */ + public static function getDispatcher(): EventDispatcher + { + if (self::$dispatcher) { + return self::$dispatcher; + } + + return self::$dispatcher = self::createDispatcher(); + } + + /** + * createDispatcher. + * + * @author yansongda + * + * @return EventDispatcher + */ + public static function createDispatcher(): EventDispatcher + { + return new EventDispatcher(); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequested.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequested.php new file mode 100644 index 0000000..20eb3d5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequested.php @@ -0,0 +1,36 @@ +endpoint = $endpoint; + $this->result = $result; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequesting.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequesting.php new file mode 100644 index 0000000..499746a --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/ApiRequesting.php @@ -0,0 +1,36 @@ +endpoint = $endpoint; + $this->payload = $payload; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/Event.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/Event.php new file mode 100644 index 0000000..9d1c502 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/Event.php @@ -0,0 +1,43 @@ + + * + * @param string $driver + * @param string $gateway + */ + public function __construct(string $driver, string $gateway) + { + $this->driver = $driver; + $this->gateway = $gateway; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/MethodCalled.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/MethodCalled.php new file mode 100644 index 0000000..424b99b --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/MethodCalled.php @@ -0,0 +1,38 @@ + + * + * @param string $driver + * @param string $gateway + * @param string $endpoint + * @param array $payload + */ + public function __construct(string $driver, string $gateway, string $endpoint, array $payload = []) + { + $this->endpoint = $endpoint; + $this->payload = $payload; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarted.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarted.php new file mode 100644 index 0000000..c7f2c0f --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarted.php @@ -0,0 +1,36 @@ +endpoint = $endpoint; + $this->payload = $payload; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarting.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarting.php new file mode 100644 index 0000000..c44a540 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/PayStarting.php @@ -0,0 +1,27 @@ +params = $params; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/RequestReceived.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/RequestReceived.php new file mode 100644 index 0000000..d37c461 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/RequestReceived.php @@ -0,0 +1,29 @@ + + * + * @param string $driver + * @param string $gateway + * @param array $data + */ + public function __construct(string $driver, string $gateway, array $data) + { + $this->data = $data; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/SignFailed.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/SignFailed.php new file mode 100644 index 0000000..8311864 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Events/SignFailed.php @@ -0,0 +1,29 @@ + + * + * @param string $driver + * @param string $gateway + * @param array $data + */ + public function __construct(string $driver, string $gateway, array $data) + { + $this->data = $data; + + parent::__construct($driver, $gateway); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/BusinessException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/BusinessException.php new file mode 100644 index 0000000..8e94a4f --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/BusinessException.php @@ -0,0 +1,19 @@ + + * + * @param string $message + * @param array|string $raw + */ + public function __construct($message, $raw = []) + { + parent::__construct('ERROR_BUSINESS: '.$message, $raw, self::ERROR_BUSINESS); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/Exception.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/Exception.php new file mode 100644 index 0000000..b5ddd0d --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/Exception.php @@ -0,0 +1,44 @@ + + * + * @param string $message + * @param array|string $raw + * @param int|string $code + */ + public function __construct($message = '', $raw = [], $code = self::UNKNOWN_ERROR) + { + $message = $message === '' ? 'Unknown Error' : $message; + $this->raw = is_array($raw) ? $raw : [$raw]; + + parent::__construct($message, intval($code)); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/GatewayException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/GatewayException.php new file mode 100644 index 0000000..e844801 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/GatewayException.php @@ -0,0 +1,20 @@ + + * + * @param string $message + * @param array|string $raw + * @param int $code + */ + public function __construct($message, $raw = [], $code = self::ERROR_GATEWAY) + { + parent::__construct('ERROR_GATEWAY: '.$message, $raw, $code); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidArgumentException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidArgumentException.php new file mode 100644 index 0000000..2ecb16b --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * @param string $message + * @param array|string $raw + */ + public function __construct($message, $raw = []) + { + parent::__construct('INVALID_ARGUMENT: '.$message, $raw, self::INVALID_ARGUMENT); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidConfigException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidConfigException.php new file mode 100644 index 0000000..5719310 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidConfigException.php @@ -0,0 +1,19 @@ + + * + * @param string $message + * @param array|string $raw + */ + public function __construct($message, $raw = []) + { + parent::__construct('INVALID_CONFIG: '.$message, $raw, self::INVALID_CONFIG); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidGatewayException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidGatewayException.php new file mode 100644 index 0000000..3f4067d --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidGatewayException.php @@ -0,0 +1,19 @@ + + * + * @param string $message + * @param array|string $raw + */ + public function __construct($message, $raw = []) + { + parent::__construct('INVALID_GATEWAY: '.$message, $raw, self::INVALID_GATEWAY); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidSignException.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidSignException.php new file mode 100644 index 0000000..135c8a5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Exceptions/InvalidSignException.php @@ -0,0 +1,19 @@ + + * + * @param string $message + * @param array|string $raw + */ + public function __construct($message, $raw = []) + { + parent::__construct('INVALID_SIGN: '.$message, $raw, self::INVALID_SIGN); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay.php new file mode 100644 index 0000000..ebd9038 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay.php @@ -0,0 +1,446 @@ + 'https://openapi.alipay.com/gateway.do?charset=utf-8', + self::MODE_DEV => 'https://openapi.alipaydev.com/gateway.do?charset=utf-8', + ]; + + /** + * Alipay payload. + * + * @var array + */ + protected $payload; + + /** + * Alipay gateway. + * + * @var string + */ + protected $gateway; + + /** + * extends. + * + * @var array + */ + protected $extends; + + /** + * Bootstrap. + * + * @author yansongda + * + * @param Config $config + */ + public function __construct(Config $config) + { + $this->gateway = Support::create($config)->getBaseUri(); + $this->payload = [ + 'app_id' => $config->get('app_id'), + 'method' => '', + 'format' => 'JSON', + 'charset' => 'utf-8', + 'sign_type' => 'RSA2', + 'version' => '1.0', + 'return_url' => $config->get('return_url'), + 'notify_url' => $config->get('notify_url'), + 'timestamp' => date('Y-m-d H:i:s'), + 'sign' => '', + 'biz_content' => '', + 'app_auth_token' => $config->get('app_auth_token'), + 'app_cert_sn' => $config->get('app_cert_sn'), + 'alipay_root_cert_sn' => $config->get('alipay_root_cert_sn'), + + ]; + } + + /** + * Magic pay. + * + * @author yansongda + * + * @param string $method + * @param array $params + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidConfigException + * @throws InvalidGatewayException + * @throws InvalidSignException + * + * @return Response|Collection + */ + public function __call($method, $params) + { + if (isset($this->extends[$method])) { + return $this->makeExtend($method, ...$params); + } + + return $this->pay($method, ...$params); + } + + /** + * Pay an order. + * + * @author yansongda + * + * @param string $gateway + * @param array $params + * + * @throws InvalidGatewayException + * + * @return Response|Collection + */ + public function pay($gateway, $params = []) + { + Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params)); + + $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url']; + $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url']; + + unset($params['return_url'], $params['notify_url']); + + $this->payload['biz_content'] = json_encode($params); + + $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway'; + + if (class_exists($gateway)) { + return $this->makePay($gateway); + } + + throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists"); + } + + /** + * Verify sign. + * + * @author yansongda + * + * @param null|array $data + * @param bool $refund + * + * @throws InvalidSignException + * @throws InvalidConfigException + * + * @return Collection + */ + public function verify($data = null, bool $refund = false): Collection + { + if (is_null($data)) { + $request = Request::createFromGlobals(); + + $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all(); + } + + if (isset($data['fund_bill_list'])) { + $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']); + } + + Events::dispatch(new Events\RequestReceived('Alipay', '', $data)); + + if (Support::verifySign($data)) { + return new Collection($data); + } + + Events::dispatch(new Events\SignFailed('Alipay', '', $data)); + + throw new InvalidSignException('Alipay Sign Verify FAILED', $data); + } + + /** + * Query an order. + * + * @author yansongda + * + * @param string|array $order + * @param string $type + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function find($order, string $type = 'wap'): Collection + { + $gateway = get_class($this).'\\'.Str::studly($type).'Gateway'; + + if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) { + throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method"); + } + + $config = call_user_func([new $gateway(), 'find'], $order); + + $this->payload['method'] = $config['method']; + $this->payload['biz_content'] = $config['biz_content']; + $this->payload['sign'] = Support::generateSign($this->payload); + + Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload)); + + return Support::requestApi($this->payload); + } + + /** + * Refund an order. + * + * @author yansongda + * + * @param array $order + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function refund(array $order): Collection + { + $this->payload['method'] = 'alipay.trade.refund'; + $this->payload['biz_content'] = json_encode($order); + $this->payload['sign'] = Support::generateSign($this->payload); + + Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload)); + + return Support::requestApi($this->payload); + } + + /** + * Cancel an order. + * + * @author yansongda + * + * @param array|string $order + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function cancel($order): Collection + { + $this->payload['method'] = 'alipay.trade.cancel'; + $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]); + $this->payload['sign'] = Support::generateSign($this->payload); + + Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload)); + + return Support::requestApi($this->payload); + } + + /** + * Close an order. + * + * @param string|array $order + * + * @author yansongda + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function close($order): Collection + { + $this->payload['method'] = 'alipay.trade.close'; + $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]); + $this->payload['sign'] = Support::generateSign($this->payload); + + Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload)); + + return Support::requestApi($this->payload); + } + + /** + * Download bill. + * + * @author yansongda + * + * @param string|array $bill + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return string + */ + public function download($bill): string + { + $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query'; + $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]); + $this->payload['sign'] = Support::generateSign($this->payload); + + Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload)); + + $result = Support::requestApi($this->payload); + + return ($result instanceof Collection) ? $result->get('bill_download_url') : ''; + } + + /** + * Reply success to alipay. + * + * @author yansongda + * + * @return Response + */ + public function success(): Response + { + Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway)); + + return Response::create('success'); + } + + /** + * extend. + * + * @author yansongda + * + * @param string $method + * @param callable $function + * @param bool $now + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection|null + */ + public function extend(string $method, callable $function, bool $now = true): ?Collection + { + if (!$now && !method_exists($this, $method)) { + $this->extends[$method] = $function; + + return null; + } + + $customize = $function($this->payload); + + if (!is_array($customize) && !($customize instanceof Collection)) { + throw new InvalidArgumentException('Return Type Must Be Array Or Collection'); + } + + Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize)); + + if (is_array($customize)) { + $this->payload = $customize; + $this->payload['sign'] = Support::generateSign($this->payload); + + return Support::requestApi($this->payload); + } + + return $customize; + } + + /** + * Make pay gateway. + * + * @author yansongda + * + * @param string $gateway + * + * @throws InvalidGatewayException + * + * @return Response|Collection + */ + protected function makePay(string $gateway) + { + $app = new $gateway(); + + if ($app instanceof GatewayInterface) { + return $app->pay($this->gateway, array_filter($this->payload, function ($value) { + return $value !== '' && !is_null($value); + })); + } + + throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface"); + } + + /** + * makeExtend. + * + * @author yansongda + * + * @param string $method + * @param array $params + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + protected function makeExtend(string $method, array ...$params): Collection + { + $params = count($params) >= 1 ? $params[0] : $params; + + $function = $this->extends[$method]; + + $customize = $function($this->payload, $params); + + if (!is_array($customize) && !($customize instanceof Collection)) { + throw new InvalidArgumentException('Return Type Must Be Array Or Collection'); + } + + Events::dispatch(new Events\MethodCalled( + 'Alipay', + 'extend - '.$method, + $this->gateway, + is_array($customize) ? $customize : $customize->toArray() + )); + + if (is_array($customize)) { + $this->payload = $customize; + $this->payload['sign'] = Support::generateSign($this->payload); + + return Support::requestApi($this->payload); + } + + return $customize; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/AppGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/AppGateway.php new file mode 100644 index 0000000..19b00a4 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/AppGateway.php @@ -0,0 +1,37 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws InvalidConfigException + * + * @return Response + */ + public function pay($endpoint, array $payload): Response + { + $payload['method'] = 'alipay.trade.app.pay'; + $payload['biz_content'] = json_encode(array_merge( + json_decode($payload['biz_content'], true), + ['product_code' => 'QUICK_MSECURITY_PAY'] + )); + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'App', $endpoint, $payload)); + + return Response::create(http_build_query($payload)); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/MiniGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/MiniGateway.php new file mode 100644 index 0000000..1fcf260 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/MiniGateway.php @@ -0,0 +1,45 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @link https://docs.alipay.com/mini/introduce/pay + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + if (empty(json_decode($payload['biz_content'], true)['buyer_id'])) { + throw new InvalidArgumentException('buyer_id required'); + } + + $payload['method'] = 'alipay.trade.create'; + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'Mini', $endpoint, $payload)); + + return Support::requestApi($payload); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/PosGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/PosGateway.php new file mode 100644 index 0000000..40c345b --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/PosGateway.php @@ -0,0 +1,44 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['method'] = 'alipay.trade.pay'; + $payload['biz_content'] = json_encode(array_merge( + json_decode($payload['biz_content'], true), + [ + 'product_code' => 'FACE_TO_FACE_PAYMENT', + 'scene' => 'bar_code', + ] + )); + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'Pos', $endpoint, $payload)); + + return Support::requestApi($payload); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/RefundGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/RefundGateway.php new file mode 100644 index 0000000..c8d202f --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/RefundGateway.php @@ -0,0 +1,23 @@ + + * + * @param $order + * + * @return array + */ + public function find($order): array + { + return [ + 'method' => 'alipay.trade.fastpay.refund.query', + 'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]), + ]; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/ScanGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/ScanGateway.php new file mode 100644 index 0000000..4aa07e6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/ScanGateway.php @@ -0,0 +1,41 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['method'] = 'alipay.trade.precreate'; + $payload['biz_content'] = json_encode(array_merge( + json_decode($payload['biz_content'], true), + ['product_code' => ''] + )); + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'Scan', $endpoint, $payload)); + + return Support::requestApi($payload); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/Support.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/Support.php new file mode 100644 index 0000000..abf7dc5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/Support.php @@ -0,0 +1,356 @@ + + * + * @property string app_id alipay app_id + * @property string ali_public_key + * @property string private_key + * @property array http http options + * @property string mode current mode + * @property array log log options + */ +class Support +{ + use HasHttpRequest; + + /** + * Alipay gateway. + * + * @var string + */ + protected $baseUri; + + /** + * Config. + * + * @var Config + */ + protected $config; + + /** + * Instance. + * + * @var Support + */ + private static $instance; + + /** + * Bootstrap. + * + * @author yansongda + * + * @param Config $config + */ + private function __construct(Config $config) + { + $this->baseUri = Alipay::URL[$config->get('mode', Alipay::MODE_NORMAL)]; + $this->config = $config; + + $this->setHttpOptions(); + } + + /** + * __get. + * + * @author yansongda + * + * @param $key + * + * @return mixed|null|Config + */ + public function __get($key) + { + return $this->getConfig($key); + } + + /** + * create. + * + * @author yansongda + * + * @param Config $config + * + * @return Support + */ + public static function create(Config $config) + { + if (php_sapi_name() === 'cli' || !(self::$instance instanceof self)) { + self::$instance = new self($config); + } + + return self::$instance; + } + + /** + * clear. + * + * @author yansongda + * + * @return void + */ + public function clear() + { + self::$instance = null; + } + + /** + * Get Alipay API result. + * + * @author yansongda + * + * @param array $data + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public static function requestApi(array $data): Collection + { + Events::dispatch(new Events\ApiRequesting('Alipay', '', self::$instance->getBaseUri(), $data)); + + $data = array_filter($data, function ($value) { + return ($value == '' || is_null($value)) ? false : true; + }); + + $result = json_decode(self::$instance->post('', $data), true); + + Events::dispatch(new Events\ApiRequested('Alipay', '', self::$instance->getBaseUri(), $result)); + + return self::processingApiResult($data, $result); + } + + /** + * Generate sign. + * + * @author yansongda + * + * @param array $params + * + * @throws InvalidConfigException + * + * @return string + */ + public static function generateSign(array $params): string + { + $privateKey = self::$instance->private_key; + + if (is_null($privateKey)) { + throw new InvalidConfigException('Missing Alipay Config -- [private_key]'); + } + + if (Str::endsWith($privateKey, '.pem')) { + $privateKey = openssl_pkey_get_private( + Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey + ); + } else { + $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n". + wordwrap($privateKey, 64, "\n", true). + "\n-----END RSA PRIVATE KEY-----"; + } + + openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256); + + $sign = base64_encode($sign); + + Log::debug('Alipay Generate Sign', [$params, $sign]); + + if (is_resource($privateKey)) { + openssl_free_key($privateKey); + } + + return $sign; + } + + /** + * Verify sign. + * + * @author yansongda + * + * @param array $data + * @param bool $sync + * @param string|null $sign + * + * @throws InvalidConfigException + * + * @return bool + */ + public static function verifySign(array $data, $sync = false, $sign = null): bool + { + $publicKey = self::$instance->ali_public_key; + + if (is_null($publicKey)) { + throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]'); + } + + if (Str::endsWith($publicKey, '.pem')) { + $publicKey = openssl_pkey_get_public( + Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey + ); + } else { + $publicKey = "-----BEGIN PUBLIC KEY-----\n". + wordwrap($publicKey, 64, "\n", true). + "\n-----END PUBLIC KEY-----"; + } + + $sign = $sign ?? $data['sign']; + + $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true); + + $isVerify = openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256) === 1; + + if (is_resource($publicKey)) { + openssl_free_key($publicKey); + } + + return $isVerify; + } + + /** + * Get signContent that is to be signed. + * + * @author yansongda + * + * @param array $data + * @param bool $verify + * + * @return string + */ + public static function getSignContent(array $data, $verify = false): string + { + ksort($data); + + $stringToBeSigned = ''; + foreach ($data as $k => $v) { + if ($verify && $k != 'sign' && $k != 'sign_type') { + $stringToBeSigned .= $k.'='.$v.'&'; + } + if (!$verify && $v !== '' && !is_null($v) && $k != 'sign' && '@' != substr($v, 0, 1)) { + $stringToBeSigned .= $k.'='.$v.'&'; + } + } + + Log::debug('Alipay Generate Sign Content Before Trim', [$data, $stringToBeSigned]); + + return trim($stringToBeSigned, '&'); + } + + /** + * Convert encoding. + * + * @author yansongda + * + * @param string|array $data + * @param string $to + * @param string $from + * + * @return array + */ + public static function encoding($data, $to = 'utf-8', $from = 'gb2312'): array + { + return Arr::encoding((array) $data, $to, $from); + } + + /** + * Get service config. + * + * @author yansongda + * + * @param null|string $key + * @param null|mixed $default + * + * @return mixed|null + */ + public function getConfig($key = null, $default = null) + { + if (is_null($key)) { + return $this->config->all(); + } + + if ($this->config->has($key)) { + return $this->config[$key]; + } + + return $default; + } + + /** + * Get Base Uri. + * + * @author yansongda + * + * @return string + */ + public function getBaseUri() + { + return $this->baseUri; + } + + /** + * processingApiResult. + * + * @author yansongda + * + * @param $data + * @param $result + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + protected static function processingApiResult($data, $result): Collection + { + $method = str_replace('.', '_', $data['method']).'_response'; + + if (!isset($result['sign']) || $result[$method]['code'] != '10000') { + throw new GatewayException( + 'Get Alipay API Error:'.$result[$method]['msg']. + (isset($result[$method]['sub_code']) ? (' - '.$result[$method]['sub_code']) : ''), + $result + ); + } + + if (self::verifySign($result[$method], true, $result['sign'])) { + return new Collection($result[$method]); + } + + Events::dispatch(new Events\SignFailed('Alipay', '', $result)); + + throw new InvalidSignException('Alipay Sign Verify FAILED', $result); + } + + /** + * Set Http options. + * + * @author yansongda + * + * @return self + */ + protected function setHttpOptions(): self + { + if ($this->config->has('http') && is_array($this->config->get('http'))) { + $this->config->forget('http.base_uri'); + $this->httpOptions = $this->config->get('http'); + } + + return $this; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/TransferGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/TransferGateway.php new file mode 100644 index 0000000..022b882 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/TransferGateway.php @@ -0,0 +1,67 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidConfigException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + if($payload['version'] > 0){ + $payload['method'] = 'alipay.fund.trans.uni.transfer'; + $payload['biz_content'] = json_encode(array_merge( + json_decode($payload['biz_content'], true), + ['product_code' => 'TRANS_ACCOUNT_NO_PWD'] + )); + }else{ + $payload['method'] = 'alipay.fund.trans.toaccount.transfer'; + $payload['biz_content'] = json_encode(array_merge( + json_decode($payload['biz_content'], true), + ['product_code' => ''] + )); + } + unset($payload['version']); + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'Transfer', $endpoint, $payload)); + + return Support::requestApi($payload); + } + + /** + * Find. + * + * @author yansongda + * + * @param $order + * + * @return array + */ + public function find($order): array + { + return [ + 'method' => 'alipay.fund.trans.order.query', + 'biz_content' => json_encode(is_array($order) ? $order : ['out_biz_no' => $order]), + ]; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WapGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WapGateway.php new file mode 100644 index 0000000..9d81ec3 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WapGateway.php @@ -0,0 +1,30 @@ + + * + * @return string + */ + protected function getMethod(): string + { + return 'alipay.trade.wap.pay'; + } + + /** + * Get productCode config. + * + * @author yansongda + * + * @return string + */ + protected function getProductCode(): string + { + return 'QUICK_WAP_WAY'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WebGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WebGateway.php new file mode 100644 index 0000000..b949035 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Alipay/WebGateway.php @@ -0,0 +1,111 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws InvalidConfigException + * + * @return Response + */ + public function pay($endpoint, array $payload): Response + { + $biz_array = json_decode($payload['biz_content'], true); + $biz_array['product_code'] = $this->getProductCode(); + + $method = $biz_array['http_method'] ?? 'POST'; + + unset($biz_array['http_method']); + + $payload['method'] = $this->getMethod(); + $payload['biz_content'] = json_encode($biz_array); + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Alipay', 'Web/Wap', $endpoint, $payload)); + + return $this->buildPayHtml($endpoint, $payload, $method); + } + + /** + * Find. + * + * @author yansongda + * + * @param $order + * + * @return array + */ + public function find($order): array + { + return [ + 'method' => 'alipay.trade.query', + 'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]), + ]; + } + + /** + * Build Html response. + * + * @author yansongda + * + * @param string $endpoint + * @param array $payload + * @param string $method + * + * @return Response + */ + protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response + { + if (strtoupper($method) === 'GET') { + return RedirectResponse::create($endpoint.'&'.http_build_query($payload)); + } + + $sHtml = "
"; + foreach ($payload as $key => $val) { + $val = str_replace("'", ''', $val); + $sHtml .= ""; + } + $sHtml .= "
"; + $sHtml .= ""; + + return Response::create($sHtml); + } + + /** + * Get method config. + * + * @author yansongda + * + * @return string + */ + protected function getMethod(): string + { + return 'alipay.trade.page.pay'; + } + + /** + * Get productCode config. + * + * @author yansongda + * + * @return string + */ + protected function getProductCode(): string + { + return 'FAST_INSTANT_TRADE_PAY'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat.php new file mode 100644 index 0000000..3631462 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat.php @@ -0,0 +1,387 @@ + 'https://api.mch.weixin.qq.com/', + self::MODE_DEV => 'https://api.mch.weixin.qq.com/sandboxnew/', + self::MODE_HK => 'https://apihk.mch.weixin.qq.com/', + self::MODE_SERVICE => 'https://api.mch.weixin.qq.com/', + self::MODE_US => 'https://apius.mch.weixin.qq.com/', + ]; + + /** + * Wechat payload. + * + * @var array + */ + protected $payload; + + /** + * Wechat gateway. + * + * @var string + */ + protected $gateway; + + /** + * Bootstrap. + * + * @author yansongda + * + * @param Config $config + * + * @throws Exception + */ + public function __construct(Config $config) + { + $this->gateway = Support::create($config)->getBaseUri(); + $this->payload = [ + 'appid' => $config->get('app_id', ''), + 'mch_id' => $config->get('mch_id', ''), + 'nonce_str' => Str::random(), + 'notify_url' => $config->get('notify_url', ''), + 'sign' => '', + 'trade_type' => '', + 'spbill_create_ip' => Request::createFromGlobals()->getClientIp(), + ]; + + if ($config->get('mode', self::MODE_NORMAL) === static::MODE_SERVICE) { + $this->payload = array_merge($this->payload, [ + 'sub_mch_id' => $config->get('sub_mch_id'), + 'sub_appid' => $config->get('sub_app_id', ''), + ]); + } + } + + /** + * Magic pay. + * + * @author yansongda + * + * @param string $method + * @param string $params + * + * @throws InvalidGatewayException + * + * @return Response|Collection + */ + public function __call($method, $params) + { + return self::pay($method, ...$params); + } + + /** + * Pay an order. + * + * @author yansongda + * + * @param string $gateway + * @param array $params + * + * @throws InvalidGatewayException + * + * @return Response|Collection + */ + public function pay($gateway, $params = []) + { + Events::dispatch(new Events\PayStarting('Wechat', $gateway, $params)); + + $this->payload = array_merge($this->payload, $params); + + $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway'; + + if (class_exists($gateway)) { + return $this->makePay($gateway); + } + + throw new InvalidGatewayException("Pay Gateway [{$gateway}] Not Exists"); + } + + /** + * Verify data. + * + * @author yansongda + * + * @param string|null $content + * @param bool $refund + * + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection + */ + public function verify($content = null, bool $refund = false): Collection + { + $content = $content ?? Request::createFromGlobals()->getContent(); + + Events::dispatch(new Events\RequestReceived('Wechat', '', [$content])); + + $data = Support::fromXml($content); + if ($refund) { + $decrypt_data = Support::decryptRefundContents($data['req_info']); + $data = array_merge(Support::fromXml($decrypt_data), $data); + } + + Log::debug('Resolved The Received Wechat Request Data', $data); + + if ($refund || Support::generateSign($data) === $data['sign']) { + return new Collection($data); + } + + Events::dispatch(new Events\SignFailed('Wechat', '', $data)); + + throw new InvalidSignException('Wechat Sign Verify FAILED', $data); + } + + /** + * Query an order. + * + * @author yansongda + * + * @param string|array $order + * @param string $type + * + * @throws GatewayException + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection + */ + public function find($order, string $type = 'wap'): Collection + { + if ($type != 'wap') { + unset($this->payload['spbill_create_ip']); + } + + $gateway = get_class($this).'\\'.Str::studly($type).'Gateway'; + + if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) { + throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method"); + } + + $config = call_user_func([new $gateway(), 'find'], $order); + + $this->payload = Support::filterPayload($this->payload, $config['order']); + + Events::dispatch(new Events\MethodCalled('Wechat', 'Find', $this->gateway, $this->payload)); + + return Support::requestApi( + $config['endpoint'], + $this->payload, + $config['cert'] + ); + } + + /** + * Refund an order. + * + * @author yansongda + * + * @param array $order + * + * @throws GatewayException + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection + */ + public function refund(array $order): Collection + { + $this->payload = Support::filterPayload($this->payload, $order, true); + + Events::dispatch(new Events\MethodCalled('Wechat', 'Refund', $this->gateway, $this->payload)); + + return Support::requestApi( + 'secapi/pay/refund', + $this->payload, + true + ); + } + + /** + * Cancel an order. + * + * @author yansongda + * + * @param array $order + * + * @throws GatewayException + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection + */ + public function cancel($order): Collection + { + unset($this->payload['spbill_create_ip']); + + $this->payload = Support::filterPayload($this->payload, $order, true); + + Events::dispatch(new Events\MethodCalled('Wechat', 'Cancel', $this->gateway, $this->payload)); + + return Support::requestApi( + 'secapi/pay/reverse', + $this->payload, + true + ); + } + + /** + * Close an order. + * + * @author yansongda + * + * @param string|array $order + * + * @throws GatewayException + * @throws InvalidSignException + * @throws InvalidArgumentException + * + * @return Collection + */ + public function close($order): Collection + { + unset($this->payload['spbill_create_ip']); + + $this->payload = Support::filterPayload($this->payload, $order); + + Events::dispatch(new Events\MethodCalled('Wechat', 'Close', $this->gateway, $this->payload)); + + return Support::requestApi('pay/closeorder', $this->payload); + } + + /** + * Echo success to server. + * + * @author yansongda + * + * @throws InvalidArgumentException + * + * @return Response + */ + public function success(): Response + { + Events::dispatch(new Events\MethodCalled('Wechat', 'Success', $this->gateway)); + + return Response::create( + Support::toXml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']), + 200, + ['Content-Type' => 'application/xml'] + ); + } + + /** + * Download the bill. + * + * @author yansongda + * + * @param array $params + * + * @throws GatewayException + * @throws InvalidArgumentException + * + * @return string + */ + public function download(array $params): string + { + unset($this->payload['spbill_create_ip']); + + $this->payload = Support::filterPayload($this->payload, $params, true); + + Events::dispatch(new Events\MethodCalled('Wechat', 'Download', $this->gateway, $this->payload)); + + $result = Support::getInstance()->post( + 'pay/downloadbill', + Support::getInstance()->toXml($this->payload) + ); + + if (is_array($result)) { + throw new GatewayException('Get Wechat API Error: '.$result['return_msg'], $result); + } + + return $result; + } + + /** + * Make pay gateway. + * + * @author yansongda + * + * @param string $gateway + * + * @throws InvalidGatewayException + * + * @return Response|Collection + */ + protected function makePay($gateway) + { + $app = new $gateway(); + + if ($app instanceof GatewayInterface) { + return $app->pay($this->gateway, array_filter($this->payload, function ($value) { + return $value !== '' && !is_null($value); + })); + } + + throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface"); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/AppGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/AppGateway.php new file mode 100644 index 0000000..afdb166 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/AppGateway.php @@ -0,0 +1,67 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * @throws Exception + * + * @return Response + */ + public function pay($endpoint, array $payload): Response + { + $payload['appid'] = Support::getInstance()->appid; + $payload['trade_type'] = $this->getTradeType(); + + if ($this->mode === WeChat::MODE_SERVICE) { + $payload['sub_appid'] = Support::getInstance()->sub_appid; + } + + $pay_request = [ + 'appid' => $this->mode === WeChat::MODE_SERVICE ? $payload['sub_appid'] : $payload['appid'], + 'partnerid' => $this->mode === WeChat::MODE_SERVICE ? $payload['sub_mch_id'] : $payload['mch_id'], + 'prepayid' => $this->preOrder($payload)->get('prepay_id'), + 'timestamp' => strval(time()), + 'noncestr' => Str::random(), + 'package' => 'Sign=WXPay', + ]; + $pay_request['sign'] = Support::generateSign($pay_request); + + Events::dispatch(new Events\PayStarted('Wechat', 'App', $endpoint, $pay_request)); + + return JsonResponse::create($pay_request); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return 'APP'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Gateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Gateway.php new file mode 100644 index 0000000..decf603 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Gateway.php @@ -0,0 +1,93 @@ + + * + * @throws InvalidArgumentException + */ + public function __construct() + { + $this->mode = Support::getInstance()->mode; + } + + /** + * Pay an order. + * + * @author yansongda + * + * @param string $endpoint + * @param array $payload + * + * @return Collection + */ + abstract public function pay($endpoint, array $payload); + + /** + * Find. + * + * @author yansongda + * + * @param string|array $order + * + * @return array + */ + public function find($order): array + { + return [ + 'endpoint' => 'pay/orderquery', + 'order' => is_array($order) ? $order : ['out_trade_no' => $order], + 'cert' => false, + ]; + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + abstract protected function getTradeType(); + + /** + * Schedule an order. + * + * @author yansongda + * + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + protected function preOrder($payload): Collection + { + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\MethodCalled('Wechat', 'PreOrder', '', $payload)); + + return Support::requestApi('pay/unifiedorder', $payload); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/GroupRedpackGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/GroupRedpackGateway.php new file mode 100644 index 0000000..c22a1ad --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/GroupRedpackGateway.php @@ -0,0 +1,62 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['wxappid'] = $payload['appid']; + $payload['amt_type'] = 'ALL_RAND'; + + if ($this->mode === WeChat::MODE_SERVICE) { + $payload['msgappid'] = $payload['appid']; + } + + unset($payload['appid'], $payload['trade_type'], + $payload['notify_url'], $payload['spbill_create_ip']); + + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Wechat', 'Group Redpack', $endpoint, $payload)); + + return Support::requestApi( + 'mmpaymkttransfers/sendgroupredpack', + $payload, + true + ); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return ''; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MiniappGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MiniappGateway.php new file mode 100644 index 0000000..e6f37ef --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MiniappGateway.php @@ -0,0 +1,38 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['appid'] = Support::getInstance()->miniapp_id; + + if ($this->mode === WeChat::MODE_SERVICE) { + $payload['sub_appid'] = Support::getInstance()->sub_miniapp_id; + $this->payRequestUseSubAppId = true; + } + + return parent::pay($endpoint, $payload); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MpGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MpGateway.php new file mode 100644 index 0000000..868cebe --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/MpGateway.php @@ -0,0 +1,64 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * @throws Exception + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['trade_type'] = $this->getTradeType(); + + $pay_request = [ + 'appId' => !$this->payRequestUseSubAppId ? $payload['appid'] : $payload['sub_appid'], + 'timeStamp' => strval(time()), + 'nonceStr' => Str::random(), + 'package' => 'prepay_id='.$this->preOrder($payload)->get('prepay_id'), + 'signType' => 'MD5', + ]; + $pay_request['paySign'] = Support::generateSign($pay_request); + + Events::dispatch(new Events\PayStarted('Wechat', 'JSAPI', $endpoint, $pay_request)); + + return new Collection($pay_request); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return 'JSAPI'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/PosGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/PosGateway.php new file mode 100644 index 0000000..00a29b7 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/PosGateway.php @@ -0,0 +1,49 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + unset($payload['trade_type'], $payload['notify_url']); + + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Wechat', 'Pos', $endpoint, $payload)); + + return Support::requestApi('pay/micropay', $payload); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return 'MICROPAY'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RedpackGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RedpackGateway.php new file mode 100644 index 0000000..4db8295 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RedpackGateway.php @@ -0,0 +1,73 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + + + + $payload['wxappid'] = $payload['appid']; + + if (php_sapi_name() !== 'cli') { + $payload['client_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR'); + } + + if ($this->mode === WeChat::MODE_SERVICE) { + $payload['msgappid'] = $payload['appid']; + } + //判断是小程序红包 OR 公众号红包 默认公众号 + $api = 'sendredpack'; + if ($payload['type'] == 'miniapp') $api = 'sendminiprogramhb'; + //删除多余的信息 + unset($payload['appid'], $payload['trade_type'], + $payload['notify_url'], $payload['spbill_create_ip'], $payload['type']); + + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Wechat', 'Redpack', $endpoint, $payload)); + + + return Support::requestApi( + "mmpaymkttransfers/{$api}", + $payload, + true + ); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return ''; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RefundGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RefundGateway.php new file mode 100644 index 0000000..1a2bdf6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/RefundGateway.php @@ -0,0 +1,57 @@ + + * + * @param $order + * + * @return array + */ + public function find($order): array + { + return [ + 'endpoint' => 'pay/refundquery', + 'order' => is_array($order) ? $order : ['out_trade_no' => $order], + 'cert' => false, + ]; + } + + /** + * Pay an order. + * + * @author yansongda + * + * @param string $endpoint + * @param array $payload + * + * @throws InvalidArgumentException + * + * @return void + */ + public function pay($endpoint, array $payload) + { + throw new InvalidArgumentException('Not Support Refund In Pay'); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @throws InvalidArgumentException + * + * @return void + */ + protected function getTradeType() + { + throw new InvalidArgumentException('Not Support Refund In Pay'); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/ScanGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/ScanGateway.php new file mode 100644 index 0000000..0c436db --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/ScanGateway.php @@ -0,0 +1,49 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR'); + $payload['trade_type'] = $this->getTradeType(); + + Events::dispatch(new Events\PayStarted('Wechat', 'Scan', $endpoint, $payload)); + + return $this->preOrder($payload); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return 'NATIVE'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Support.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Support.php new file mode 100644 index 0000000..e8e6dd6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/Support.php @@ -0,0 +1,481 @@ + + * + * @property string appid + * @property string app_id + * @property string miniapp_id + * @property string sub_appid + * @property string sub_app_id + * @property string sub_miniapp_id + * @property string mch_id + * @property string sub_mch_id + * @property string key + * @property string return_url + * @property string cert_client + * @property string cert_key + * @property array log + * @property array http + * @property string mode + */ +class Support +{ + use HasHttpRequest; + + /** + * Wechat gateway. + * + * @var string + */ + protected $baseUri; + + /** + * Config. + * + * @var Config + */ + protected $config; + + /** + * Instance. + * + * @var Support + */ + private static $instance; + + /** + * Bootstrap. + * + * @author yansongda + * + * @param Config $config + */ + private function __construct(Config $config) + { + $this->baseUri = WeChat::URL[$config->get('mode', WeChat::MODE_NORMAL)]; + $this->config = $config; + + $this->setHttpOptions(); + } + + /** + * __get. + * + * @author yansongda + * + * @param $key + * + * @return mixed|null|Config + */ + public function __get($key) + { + return $this->getConfig($key); + } + + /** + * create. + * + * @author yansongda + * + * @param Config $config + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Support + */ + public static function create(Config $config) + { + if (php_sapi_name() === 'cli' || !(self::$instance instanceof self)) { + self::$instance = new self($config); + + self::setDevKey(); + } + + return self::$instance; + } + + /** + * getInstance. + * + * @author yansongda + * + * @throws InvalidArgumentException + * + * @return Support + */ + public static function getInstance() + { + if (is_null(self::$instance)) { + throw new InvalidArgumentException('You Should [Create] First Before Using'); + } + + return self::$instance; + } + + /** + * clear. + * + * @author yansongda + * + * @return void + */ + public static function clear() + { + self::$instance = null; + } + + /** + * Request wechat api. + * + * @author yansongda + * + * @param string $endpoint + * @param array $data + * @param bool $cert + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public static function requestApi($endpoint, $data, $cert = false): Collection + { + Events::dispatch(new Events\ApiRequesting('Wechat', '', self::$instance->getBaseUri().$endpoint, $data)); + + $result = self::$instance->post( + $endpoint, + self::toXml($data), + $cert ? [ + 'cert' => self::$instance->cert_client, + 'ssl_key' => self::$instance->cert_key, + ] : [] + ); + $result = is_array($result) ? $result : self::fromXml($result); + + Events::dispatch(new Events\ApiRequested('Wechat', '', self::$instance->getBaseUri().$endpoint, $result)); + + + return self::processingApiResult($endpoint, $result); + } + + /** + * Filter payload. + * + * @author yansongda + * + * @param array $payload + * @param array|string $params + * @param bool $preserve_notify_url + * + * @throws InvalidArgumentException + * + * @return array + */ + public static function filterPayload($payload, $params, $preserve_notify_url = false): array + { + $type = self::getTypeName($params['type'] ?? ''); + + $payload = array_merge( + $payload, + is_array($params) ? $params : ['out_trade_no' => $params] + ); + $payload['appid'] = self::$instance->getConfig($type, ''); + + if (self::$instance->getConfig('mode', WeChat::MODE_NORMAL) === WeChat::MODE_SERVICE) { + $payload['sub_appid'] = self::$instance->getConfig('sub_'.$type, ''); + } + + unset($payload['trade_type'], $payload['type']); + if (!$preserve_notify_url) { + unset($payload['notify_url']); + } + + $payload['sign'] = self::generateSign($payload); + + return $payload; + } + + /** + * Generate wechat sign. + * + * @author yansongda + * + * @param array $data + * + * @throws InvalidArgumentException + * + * @return string + */ + public static function generateSign($data): string + { + $key = self::$instance->key; + + if (is_null($key)) { + throw new InvalidArgumentException('Missing Wechat Config -- [key]'); + } + + ksort($data); + + $string = md5(self::getSignContent($data).'&key='.$key); + + Log::debug('Wechat Generate Sign Before UPPER', [$data, $string]); + + return strtoupper($string); + } + + /** + * Generate sign content. + * + * @author yansongda + * + * @param array $data + * + * @return string + */ + public static function getSignContent($data): string + { + $buff = ''; + + foreach ($data as $k => $v) { + $buff .= ($k != 'sign' && $v != '' && !is_array($v)) ? $k.'='.$v.'&' : ''; + } + + Log::debug('Wechat Generate Sign Content Before Trim', [$data, $buff]); + + return trim($buff, '&'); + } + + /** + * Decrypt refund contents. + * + * @author yansongda + * + * @param string $contents + * + * @return string + */ + public static function decryptRefundContents($contents): string + { + return openssl_decrypt( + base64_decode($contents), + 'AES-256-ECB', + md5(self::$instance->key), + OPENSSL_RAW_DATA + ); + } + + /** + * Convert array to xml. + * + * @author yansongda + * + * @param array $data + * + * @throws InvalidArgumentException + * + * @return string + */ + public static function toXml($data): string + { + if (!is_array($data) || count($data) <= 0) { + throw new InvalidArgumentException('Convert To Xml Error! Invalid Array!'); + } + + $xml = ''; + foreach ($data as $key => $val) { + $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'' : + '<'.$key.'>'; + } + $xml .= ''; + + return $xml; + } + + /** + * Convert xml to array. + * + * @author yansongda + * + * @param string $xml + * + * @throws InvalidArgumentException + * + * @return array + */ + public static function fromXml($xml): array + { + if (!$xml) { + throw new InvalidArgumentException('Convert To Array Error! Invalid Xml!'); + } + + libxml_disable_entity_loader(true); + + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * Get service config. + * + * @author yansongda + * + * @param null|string $key + * @param null|mixed $default + * + * @return mixed|null + */ + public function getConfig($key = null, $default = null) + { + if (is_null($key)) { + return $this->config->all(); + } + + if ($this->config->has($key)) { + return $this->config[$key]; + } + + return $default; + } + + /** + * Get app id according to param type. + * + * @author yansongda + * + * @param string $type + * + * @return string + */ + public static function getTypeName($type = ''): string + { + switch ($type) { + case '': + $type = 'app_id'; + break; + case 'app': + $type = 'appid'; + break; + default: + $type = $type.'_id'; + } + + return $type; + } + + /** + * Get Base Uri. + * + * @author yansongda + * + * @return string + */ + public function getBaseUri() + { + return $this->baseUri; + } + + /** + * processingApiResult. + * + * @author yansongda + * + * @param $endpoint + * @param array $result + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + protected static function processingApiResult($endpoint, array $result) + { + if (!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') { + throw new GatewayException( + 'Get Wechat API Error:'.($result['return_msg'] ?? $result['retmsg'] ?? ''), + $result + ); + } + + if (isset($result['result_code']) && $result['result_code'] != 'SUCCESS') { + throw new BusinessException( + 'Wechat Business Error: '.$result['err_code'].' - '.$result['err_code_des'], + $result + ); + } + + if ($endpoint === 'pay/getsignkey' || + strpos($endpoint, 'mmpaymkttransfers') !== false || + self::generateSign($result) === $result['sign']) { + return new Collection($result); + } + + Events::dispatch(new Events\SignFailed('Wechat', '', $result)); + + throw new InvalidSignException('Wechat Sign Verify FAILED', $result); + } + + /** + * setDevKey. + * + * @author yansongda + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * @throws Exception + * + * @return Support + */ + private static function setDevKey() + { + if (self::$instance->mode == WeChat::MODE_DEV) { + $data = [ + 'mch_id' => self::$instance->mch_id, + 'nonce_str' => Str::random(), + ]; + $data['sign'] = self::generateSign($data); + + $result = self::requestApi('pay/getsignkey', $data); + + self::$instance->config->set('key', $result['sandbox_signkey']); + } + + return self::$instance; + } + + /** + * Set Http options. + * + * @author yansongda + * + * @return self + */ + private function setHttpOptions(): self + { + if ($this->config->has('http') && is_array($this->config->get('http'))) { + $this->config->forget('http.base_uri'); + $this->httpOptions = $this->config->get('http'); + } + + return $this; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/TransferGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/TransferGateway.php new file mode 100644 index 0000000..4945a11 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/TransferGateway.php @@ -0,0 +1,87 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return Collection + */ + public function pay($endpoint, array $payload): Collection + { + if ($this->mode === WeChat::MODE_SERVICE) { + unset($payload['sub_mch_id'], $payload['sub_appid']); + } + + $type = Support::getTypeName($payload['type'] ?? ''); + + $payload['mch_appid'] = Support::getInstance()->getConfig($type, ''); + $payload['mchid'] = $payload['mch_id']; + + if (php_sapi_name() !== 'cli' && !isset($payload['spbill_create_ip'])) { + $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR'); + } + + unset($payload['appid'], $payload['mch_id'], $payload['trade_type'], + $payload['notify_url'], $payload['type']); + + $payload['sign'] = Support::generateSign($payload); + + Events::dispatch(new Events\PayStarted('Wechat', 'Transfer', $endpoint, $payload)); + + return Support::requestApi( + 'mmpaymkttransfers/promotion/transfers', + $payload, + true + ); + } + + /** + * Find. + * + * @author yansongda + * + * @param $order + * + * @return array + */ + public function find($order): array + { + return [ + 'endpoint' => 'mmpaymkttransfers/gettransferinfo', + 'order' => is_array($order) ? $order : ['partner_trade_no' => $order], + 'cert' => true, + ]; + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return ''; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/WapGateway.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/WapGateway.php new file mode 100644 index 0000000..ba8fc66 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Gateways/Wechat/WapGateway.php @@ -0,0 +1,52 @@ + + * + * @param string $endpoint + * @param array $payload + * + * @throws GatewayException + * @throws InvalidArgumentException + * @throws InvalidSignException + * + * @return RedirectResponse + */ + public function pay($endpoint, array $payload): RedirectResponse + { + $payload['trade_type'] = $this->getTradeType(); + + Events::dispatch(new Events\PayStarted('Wechat', 'Wap', $endpoint, $payload)); + + $mweb_url = $this->preOrder($payload)->get('mweb_url'); + + $url = is_null(Support::getInstance()->return_url) ? $mweb_url : $mweb_url. + '&redirect_url='.urlencode(Support::getInstance()->return_url); + + return RedirectResponse::create($url); + } + + /** + * Get trade type config. + * + * @author yansongda + * + * @return string + */ + protected function getTradeType(): string + { + return 'MWEB'; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Listeners/KernelLogSubscriber.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Listeners/KernelLogSubscriber.php new file mode 100644 index 0000000..a89e5b3 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Listeners/KernelLogSubscriber.php @@ -0,0 +1,142 @@ + 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return [ + Events\PayStarting::class => ['writePayStartingLog', 256], + Events\PayStarted::class => ['writePayStartedLog', 256], + Events\ApiRequesting::class => ['writeApiRequestingLog', 256], + Events\ApiRequested::class => ['writeApiRequestedLog', 256], + Events\SignFailed::class => ['writeSignFailedLog', 256], + Events\RequestReceived::class => ['writeRequestReceivedLog', 256], + Events\MethodCalled::class => ['writeMethodCalledLog', 256], + ]; + } + + /** + * writePayStartingLog. + * + * @author yansongda + * + * @param Events\PayStarting $event + * + * @return void + */ + public function writePayStartingLog(Events\PayStarting $event) + { + Log::debug("Starting To {$event->driver}", [$event->gateway, $event->params]); + } + + /** + * writePayStartedLog. + * + * @author yansongda + * + * @param Events\PayStarted $event + * + * @return void + */ + public function writePayStartedLog(Events\PayStarted $event) + { + Log::info( + "{$event->driver} {$event->gateway} Has Started", + [$event->endpoint, $event->payload] + ); + } + + /** + * writeApiRequestingLog. + * + * @author yansongda + * + * @param Events\ApiRequesting $event + * + * @return void + */ + public function writeApiRequestingLog(Events\ApiRequesting $event) + { + Log::debug("Requesting To {$event->driver} Api", [$event->endpoint, $event->payload]); + } + + /** + * writeApiRequestedLog. + * + * @author yansongda + * + * @param Events\ApiRequested $event + * + * @return void + */ + public function writeApiRequestedLog(Events\ApiRequested $event) + { + Log::debug("Result Of {$event->driver} Api", $event->result); + } + + /** + * writeSignFailedLog. + * + * @author yansongda + * + * @param Events\SignFailed $event + * + * @return void + */ + public function writeSignFailedLog(Events\SignFailed $event) + { + Log::warning("{$event->driver} Sign Verify FAILED", $event->data); + } + + /** + * writeRequestReceivedLog. + * + * @author yansongda + * + * @param Events\RequestReceived $event + * + * @return void + */ + public function writeRequestReceivedLog(Events\RequestReceived $event) + { + Log::info("Received {$event->driver} Request", $event->data); + } + + /** + * writeMethodCalledLog. + * + * @author yansongda + * + * @param Events\MethodCalled $event + * + * @return void + */ + public function writeMethodCalledLog(Events\MethodCalled $event) + { + Log::info("{$event->driver} {$event->gateway} Method Has Called", [$event->endpoint, $event->payload]); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Log.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Log.php new file mode 100644 index 0000000..c1f8c3e --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Log.php @@ -0,0 +1,49 @@ + + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public static function __callStatic($method, $args) + { + return forward_static_call_array([BaseLog::class, $method], $args); + } + + /** + * Forward call. + * + * @author yansongda + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array([BaseLog::class, $method], $args); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/src/Pay.php b/addons/weliam_smartcity/vendor/yansongda/pay/src/Pay.php new file mode 100644 index 0000000..aa4accb --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/src/Pay.php @@ -0,0 +1,142 @@ + + * + * @param array $config + * + * @throws Exception + */ + public function __construct(array $config) + { + $this->config = new Config($config); + + $this->registerLogService(); + $this->registerEventService(); + } + + /** + * Magic static call. + * + * @author yansongda + * + * @param string $method + * @param array $params + * + * @throws InvalidGatewayException + * @throws Exception + * + * @return GatewayApplicationInterface + */ + public static function __callStatic($method, $params): GatewayApplicationInterface + { + $app = new self(...$params); + + return $app->create($method); + } + + /** + * Create a instance. + * + * @author yansongda + * + * @param string $method + * + * @throws InvalidGatewayException + * + * @return GatewayApplicationInterface + */ + protected function create($method): GatewayApplicationInterface + { + $gateway = __NAMESPACE__.'\\Gateways\\'.Str::studly($method); + + if (class_exists($gateway)) { + return self::make($gateway); + } + + throw new InvalidGatewayException("Gateway [{$method}] Not Exists"); + } + + /** + * Make a gateway. + * + * @author yansongda + * + * @param string $gateway + * + * @throws InvalidGatewayException + * + * @return GatewayApplicationInterface + */ + protected function make($gateway): GatewayApplicationInterface + { + $app = new $gateway($this->config); + + if ($app instanceof GatewayApplicationInterface) { + return $app; + } + + throw new InvalidGatewayException("Gateway [{$gateway}] Must Be An Instance Of GatewayApplicationInterface"); + } + + /** + * Register log service. + * + * @author yansongda + * + * @throws Exception + */ + protected function registerLogService() + { + $logger = Log::createLogger( + $this->config->get('log.file'), + 'yansongda.pay', + $this->config->get('log.level', 'warning'), + $this->config->get('log.type', 'daily'), + $this->config->get('log.max_file', 30) + ); + + Log::setLogger($logger); + } + + /** + * Register event service. + * + * @author yansongda + * + * @return void + */ + protected function registerEventService() + { + Events::setDispatcher(Events::createDispatcher()); + + Events::addSubscriber(new KernelLogSubscriber()); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/tests/Gateways/AlipayTest.php b/addons/weliam_smartcity/vendor/yansongda/pay/tests/Gateways/AlipayTest.php new file mode 100644 index 0000000..08901c9 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/tests/Gateways/AlipayTest.php @@ -0,0 +1,18 @@ +success(); + + $this->assertInstanceOf(Response::class, $success); + $this->assertEquals('success', $success->getContent()); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/tests/PayTest.php b/addons/weliam_smartcity/vendor/yansongda/pay/tests/PayTest.php new file mode 100644 index 0000000..534e71e --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/tests/PayTest.php @@ -0,0 +1,36 @@ + 'bar']); + + $this->assertInstanceOf(Alipay::class, $alipay); + $this->assertInstanceOf(GatewayApplicationInterface::class, $alipay); + } + + public function testWechatGateway() + { + $wechat = Pay::wechat(['foo' => 'bar']); + + $this->assertInstanceOf(WeChat::class, $wechat); + $this->assertInstanceOf(GatewayApplicationInterface::class, $wechat); + } + + public function testFooGateway() + { + $this->expectException(InvalidGatewayException::class); + $this->expectExceptionMessage('INVALID_GATEWAY: Gateway [foo] Not Exists'); + + Pay::foo([]); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/pay/tests/TestCase.php b/addons/weliam_smartcity/vendor/yansongda/pay/tests/TestCase.php new file mode 100644 index 0000000..cf2d8bc --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/pay/tests/TestCase.php @@ -0,0 +1,16 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/README.md b/addons/weliam_smartcity/vendor/yansongda/supports/README.md new file mode 100644 index 0000000..debfbde --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/README.md @@ -0,0 +1,62 @@ +

Supports

+ +handle with array/config/log/guzzle etc. + +## About log + +### Register + +#### Method 1 + +A application logger can extends `Yansongda\Supports\Log` and modify `createLogger` method, the method must return instance of `Monolog\Logger`. + +```PHP +use Yansongda\Supports\Log; +use Monolog\Logger; + +class APPLICATIONLOG extends Log +{ + /** + * Make a default log instance. + * + * @author yansongda + * + * @return Logger + */ + public static function createLogger() + { + $handler = new StreamHandler('./log.log'); + $handler->setFormatter(new LineFormatter("%datetime% > %level_name% > %message% %context% %extra%\n\n")); + + $logger = new Logger('yansongda.private_number'); + $logger->pushHandler($handler); + + return $logger; + } +} +``` + +#### Method 2 + +Or, just init the log service with: + +```PHP +use Yansongda\Supports\Log; + +protected function registerLog() +{ + $logger = Log::createLogger($file, $identify, $level); + + Log::setLogger($logger); +} +``` + +### Usage + +After registerLog, you can use Log service: + +```PHP +use Yansongda\Supports\Log; + +Log::debug('test', ['test log']); +``` diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/composer.json b/addons/weliam_smartcity/vendor/yansongda/supports/composer.json new file mode 100644 index 0000000..657118f --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/composer.json @@ -0,0 +1,32 @@ +{ + "name": "yansongda/supports", + "description": "common components", + "keywords": ["support", "array", "collection", "config", "http", "guzzle", "throttle"], + "support": { + "issues": "https://github.com/yansongda/supports/issues", + "source": "https://github.com/yansongda/supports" + }, + "authors": [ + { + "name": "yansongda", + "email": "me@yansongda.cn" + } + ], + "require": { + "php": ">=5.5", + "monolog/monolog": "^1.23", + "guzzlehttp/guzzle": "^6.2" + }, + "require-dev": { + "predis/predis": "^1.1" + }, + "autoload": { + "psr-4": { + "Yansongda\\Supports\\": "src/" + } + }, + "suggest": { + "predis/predis": "Allows to use throttle feature" + }, + "license": "MIT" +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Arr.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Arr.php new file mode 100644 index 0000000..ce84332 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Arr.php @@ -0,0 +1,387 @@ + $value) { + list($innerKey, $innerValue) = call_user_func($callback, $key, $value); + $results[$innerKey] = $innerValue; + } + + return $results; + } + + /** + * Divide an array into two arrays. One with keys and the other with values. + * + * @param array $array + * + * @return array + */ + public static function divide($array) + { + return [ + array_keys($array), + array_values($array), + ]; + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @param array $array + * @param string $prepend + * + * @return array + */ + public static function dot($array, $prepend = '') + { + $results = []; + + foreach ($array as $key => $value) { + if (is_array($value)) { + $results = array_merge($results, static::dot($value, $prepend.$key.'.')); + } else { + $results[$prepend.$key] = $value; + } + } + + return $results; + } + + /** + * Get all of the given array except for a specified array of items. + * + * @param array $array + * @param array|string $keys + * + * @return array + */ + public static function except($array, $keys) + { + return array_diff_key($array, array_flip((array) $keys)); + } + + /** + * Fetch a flattened array of a nested array element. + * + * @param array $array + * @param string $key + * + * @return array + */ + public static function fetch($array, $key) + { + $results = []; + + foreach (explode('.', $key) as $segment) { + $results = []; + foreach ($array as $value) { + $value = (array) $value; + $results[] = $value[$segment]; + } + $array = array_values($results); + } + + return array_values($results); + } + + /** + * Return the first element in an array passing a given truth test. + * + * @param array $array + * @param Closure $callback + * @param mixed $default + * + * @return mixed + */ + public static function first($array, $callback, $default = null) + { + foreach ($array as $key => $value) { + if (call_user_func($callback, $key, $value)) { + return $value; + } + } + + return $default; + } + + /** + * Return the last element in an array passing a given truth test. + * + * @param array $array + * @param Closure $callback + * @param mixed $default + * + * @return mixed + */ + public static function last($array, $callback, $default = null) + { + return static::first(array_reverse($array), $callback, $default); + } + + /** + * Flatten a multi-dimensional array into a single level. + * + * @param array $array + * + * @return array + */ + public static function flatten($array) + { + $return = []; + array_walk_recursive( + $array, + function ($x) use (&$return) { + $return[] = $x; + } + ); + + return $return; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array $array + * @param array|string $keys + */ + public static function forget(&$array, $keys) + { + $original = &$array; + + foreach ((array) $keys as $key) { + $parts = explode('.', $key); + while (count($parts) > 1) { + $part = array_shift($parts); + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } + } + unset($array[array_shift($parts)]); + // clean up after each pass + $array = &$original; + } + } + + /** + * Get an item from an array using "dot" notation. + * + * @param array $array + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public static function get($array, $key, $default = null) + { + if (is_null($key)) { + return $array; + } + + if (isset($array[$key])) { + return $array[$key]; + } + + foreach (explode('.', $key) as $segment) { + if (!is_array($array) || !array_key_exists($segment, $array)) { + return $default; + } + $array = $array[$segment]; + } + + return $array; + } + + /** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array|string $keys + * + * @return array + */ + public static function only($array, $keys) + { + return array_intersect_key($array, array_flip((array) $keys)); + } + + /** + * Pluck an array of values from an array. + * + * @param array $array + * @param string $value + * @param string $key + * + * @return array + */ + public static function pluck($array, $value, $key = null) + { + $results = []; + + foreach ($array as $item) { + $itemValue = is_object($item) ? $item->{$value} : $item[$value]; + // If the key is "null", we will just append the value to the array and keep + // looping. Otherwise we will key the array using the value of the key we + // received from the developer. Then we'll return the final array form. + if (is_null($key)) { + $results[] = $itemValue; + } else { + $itemKey = is_object($item) ? $item->{$key} : $item[$key]; + $results[$itemKey] = $itemValue; + } + } + + return $results; + } + + /** + * Get a value from the array, and remove it. + * + * @param array $array + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public static function pull(&$array, $key, $default = null) + { + $value = static::get($array, $key, $default); + static::forget($array, $key); + + return $value; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param array $array + * @param string $key + * @param mixed $value + * + * @return array + */ + public static function set(&$array, $key, $value) + { + if (is_null($key)) { + return $array = $value; + } + + $keys = explode('.', $key); + + while (count($keys) > 1) { + $key = array_shift($keys); + // If the key doesn't exist at this depth, we will just create an empty array + // to hold the next value, allowing us to create the arrays to hold final + // values at the correct depth. Then we'll keep digging into the array. + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + $array = &$array[$key]; + } + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Sort the array using the given Closure. + * + * @param array $array + * @param Closure $callback + * + * @return array + */ + public static function sort($array, Closure $callback) + { + $results = []; + + foreach ($array as $key => $value) { + $results[$key] = $callback($value); + } + + return $results; + } + + /** + * Filter the array using the given Closure. + * + * @param array $array + * @param Closure $callback + * + * @return array + */ + public static function where($array, Closure $callback) + { + $filtered = []; + + foreach ($array as $key => $value) { + if (call_user_func($callback, $key, $value)) { + $filtered[$key] = $value; + } + } + + return $filtered; + } + + /** + * Convert encoding. + * + * @author yansongda + * + * @param array $array + * @param string $to_encoding + * @param string $from_encoding + * + * @return array + */ + public static function encoding($array, $to_encoding, $from_encoding = 'gb2312') + { + $encoded = []; + + foreach ($array as $key => $value) { + $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) : + mb_convert_encoding($value, $to_encoding, $from_encoding); + } + + return $encoded; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Collection.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Collection.php new file mode 100644 index 0000000..15275f5 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Collection.php @@ -0,0 +1,417 @@ + + * + * modified by yansongda + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Yansongda\Supports; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use IteratorAggregate; +use JsonSerializable; +use Serializable; + +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Serializable +{ + /** + * The collection data. + * + * @var array + */ + protected $items = []; + + /** + * set data. + * + * @param mixed $items + */ + public function __construct(array $items = []) + { + foreach ($items as $key => $value) { + $this->set($key, $value); + } + } + + /** + * To string. + * + * @return string + */ + public function __toString() + { + return $this->toJson(); + } + + /** + * Get a data by key. + * + * @param string $key + * + * @return mixed + */ + public function __get($key) + { + return $this->get($key); + } + + /** + * Assigns a value to the specified data. + * + * @param string $key + * @param mixed $value + */ + public function __set($key, $value) + { + $this->set($key, $value); + } + + /** + * Whether or not an data exists by key. + * + * @param string $key + * + * @return bool + */ + public function __isset($key) + { + return $this->has($key); + } + + /** + * Unsets an data by key. + * + * @param string $key + */ + public function __unset($key) + { + $this->forget($key); + } + + /** + * var_export. + * + * @param array $array + * + * @return array + */ + public static function __set_state(array $array = []) + { + return (new static())->all(); + } + + /** + * Return all items. + * + * @return array + */ + public function all() + { + return $this->items; + } + + /** + * Return specific items. + * + * @param array $keys + * + * @return array + */ + public function only(array $keys) + { + $return = []; + + foreach ($keys as $key) { + $value = $this->get($key); + + if (!is_null($value)) { + $return[$key] = $value; + } + } + + return $return; + } + + /** + * Get all items except for those with the specified keys. + * + * @param mixed $keys + * + * @return static + */ + public function except($keys) + { + $keys = is_array($keys) ? $keys : func_get_args(); + + return new static(Arr::except($this->items, $keys)); + } + + /** + * Merge data. + * + * @param Collection|array $items + * + * @return array + */ + public function merge($items) + { + foreach ($items as $key => $value) { + $this->set($key, $value); + } + + return $this->all(); + } + + /** + * To determine Whether the specified element exists. + * + * @param string $key + * + * @return bool + */ + public function has($key) + { + return !is_null(Arr::get($this->items, $key)); + } + + /** + * Retrieve the first item. + * + * @return mixed + */ + public function first() + { + return reset($this->items); + } + + /** + * Retrieve the last item. + * + * @return bool + */ + public function last() + { + $end = end($this->items); + + reset($this->items); + + return $end; + } + + /** + * add the item value. + * + * @param string $key + * @param mixed $value + */ + public function add($key, $value) + { + Arr::set($this->items, $key, $value); + } + + /** + * Set the item value. + * + * @param string $key + * @param mixed $value + */ + public function set($key, $value) + { + Arr::set($this->items, $key, $value); + } + + /** + * Retrieve item from Collection. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function get($key = null, $default = null) + { + return Arr::get($this->items, $key, $default); + } + + /** + * Remove item form Collection. + * + * @param string $key + */ + public function forget($key) + { + Arr::forget($this->items, $key); + } + + /** + * Build to array. + * + * @return array + */ + public function toArray() + { + return $this->all(); + } + + /** + * Build to json. + * + * @param int $option + * + * @return string + */ + public function toJson($option = JSON_UNESCAPED_UNICODE) + { + return json_encode($this->all(), $option); + } + + /** + * (PHP 5 >= 5.4.0)
+ * Specify data which should be serialized to JSON. + * + * @see http://php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource + */ + public function jsonSerialize() + { + return $this->items; + } + + /** + * (PHP 5 >= 5.1.0)
+ * String representation of object. + * + * @see http://php.net/manual/en/serializable.serialize.php + * + * @return string the string representation of the object or null + */ + public function serialize() + { + return serialize($this->items); + } + + /** + * (PHP 5 >= 5.0.0)
+ * Retrieve an external iterator. + * + * @see http://php.net/manual/en/iteratoraggregate.getiterator.php + * + * @return ArrayIterator An instance of an object implementing Iterator or + * ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->items); + } + + /** + * (PHP 5 >= 5.1.0)
+ * Count elements of an object. + * + * @see http://php.net/manual/en/countable.count.php + * + * @return int The custom count as an integer. + *

+ *

+ * The return value is cast to an integer + */ + public function count() + { + return count($this->items); + } + + /** + * (PHP 5 >= 5.1.0)
+ * Constructs the object. + * + * @see http://php.net/manual/en/serializable.unserialize.php + * + * @param string $serialized

+ * The string representation of the object. + *

+ * + * @return mixed|void + */ + public function unserialize($serialized) + { + return $this->items = unserialize($serialized); + } + + /** + * (PHP 5 >= 5.0.0)
+ * Whether a offset exists. + * + * @see http://php.net/manual/en/arrayaccess.offsetexists.php + * + * @param mixed $offset

+ * An offset to check for. + *

+ * + * @return bool true on success or false on failure. + * The return value will be casted to boolean if non-boolean was returned + */ + public function offsetExists($offset) + { + return $this->has($offset); + } + + /** + * (PHP 5 >= 5.0.0)
+ * Offset to unset. + * + * @see http://php.net/manual/en/arrayaccess.offsetunset.php + * + * @param mixed $offset

+ * The offset to unset. + *

+ */ + public function offsetUnset($offset) + { + if ($this->offsetExists($offset)) { + $this->forget($offset); + } + } + + /** + * (PHP 5 >= 5.0.0)
+ * Offset to retrieve. + * + * @see http://php.net/manual/en/arrayaccess.offsetget.php + * + * @param mixed $offset

+ * The offset to retrieve. + *

+ * + * @return mixed Can return all value types + */ + public function offsetGet($offset) + { + return $this->offsetExists($offset) ? $this->get($offset) : null; + } + + /** + * (PHP 5 >= 5.0.0)
+ * Offset to set. + * + * @see http://php.net/manual/en/arrayaccess.offsetset.php + * + * @param mixed $offset

+ * The offset to assign the value to. + *

+ * @param mixed $value

+ * The value to set. + *

+ */ + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Config.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Config.php new file mode 100644 index 0000000..a660bad --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Config.php @@ -0,0 +1,7 @@ + + * + * @param string $method + * @param array $args + * + * @throws Exception + * + * @return mixed + */ + public static function __callStatic($method, $args) + { + return forward_static_call_array([self::getLogger(), $method], $args); + } + + /** + * Forward call. + * + * @author yansongda + * + * @param string $method + * @param array $args + * + * @throws Exception + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array([self::getLogger(), $method], $args); + } + + /** + * Return the logger instance. + * + * @author yansongda + * + * @throws Exception + * + * @return LoggerInterface + */ + public static function getLogger() + { + return self::$logger ?: self::$logger = self::createLogger(); + } + + /** + * Set logger. + * + * @author yansongda + * + * @param LoggerInterface $logger + */ + public static function setLogger(LoggerInterface $logger) + { + self::$logger = $logger; + } + + /** + * Tests if logger exists. + * + * @author yansongda + * + * @return bool + */ + public static function hasLogger() + { + return self::$logger ? true : false; + } + + /** + * Make a default log instance. + * + * @author yansongda + * + * @param string $file + * @param string $identify + * @param int|string $level + * @param string $type + * @param int $max_files + * + * @throws Exception + * + * @return BaseLogger + */ + public static function createLogger($file = null, $identify = 'yansongda.supports', $level = BaseLogger::DEBUG, $type = 'daily', $max_files = 30) + { + $file = is_null($file) ? sys_get_temp_dir().'/logs/'.$identify.'.log' : $file; + + $handler = $type === 'single' ? new StreamHandler($file, $level) : new RotatingFileHandler($file, $max_files, $level); + + $handler->setFormatter( + new LineFormatter("%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n", null, false, true) + ); + + $logger = new BaseLogger($identify); + $logger->pushHandler($handler); + + return $logger; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Str.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Str.php new file mode 100644 index 0000000..efd259c --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Str.php @@ -0,0 +1,706 @@ + $val) { + $value = str_replace($val, $key, $value); + } + + return preg_replace('/[^\x20-\x7E]/u', '', $value); + } + + /** + * Get the portion of a string before a given value. + * + * @param string $subject + * @param string $search + * + * @return string + */ + public static function before($subject, $search) + { + return $search === '' ? $subject : explode($search, $subject)[0]; + } + + /** + * Convert a value to camel case. + * + * @param string $value + * + * @return string + */ + public static function camel($value) + { + if (isset(static::$camelCache[$value])) { + return static::$camelCache[$value]; + } + + return static::$camelCache[$value] = lcfirst(static::studly($value)); + } + + /** + * Determine if a given string contains a given substring. + * + * @param string $haystack + * @param string|array $needles + * + * @return bool + */ + public static function contains($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if ($needle !== '' && mb_strpos($haystack, $needle) !== false) { + return true; + } + } + + return false; + } + + /** + * Determine if a given string ends with a given substring. + * + * @param string $haystack + * @param string|array $needles + * + * @return bool + */ + public static function endsWith($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if (substr($haystack, -strlen($needle)) === (string) $needle) { + return true; + } + } + + return false; + } + + /** + * Cap a string with a single instance of a given value. + * + * @param string $value + * @param string $cap + * + * @return string + */ + public static function finish($value, $cap) + { + $quoted = preg_quote($cap, '/'); + + return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap; + } + + /** + * Determine if a given string matches a given pattern. + * + * @param string|array $pattern + * @param string $value + * + * @return bool + */ + public static function is($pattern, $value) + { + $patterns = is_array($pattern) ? $pattern : (array) $pattern; + + if (empty($patterns)) { + return false; + } + + foreach ($patterns as $pattern) { + // If the given value is an exact match we can of course return true right + // from the beginning. Otherwise, we will translate asterisks and do an + // actual pattern match against the two strings to see if they match. + if ($pattern == $value) { + return true; + } + + $pattern = preg_quote($pattern, '#'); + + // Asterisks are translated into zero-or-more regular expression wildcards + // to make it convenient to check if the strings starts with the given + // pattern such as "library/*", making any string check convenient. + $pattern = str_replace('\*', '.*', $pattern); + + if (preg_match('#^'.$pattern.'\z#u', $value) === 1) { + return true; + } + } + + return false; + } + + /** + * Convert a string to kebab case. + * + * @param string $value + * + * @return string + */ + public static function kebab($value) + { + return static::snake($value, '-'); + } + + /** + * Return the length of the given string. + * + * @param string $value + * @param string $encoding + * + * @return int + */ + public static function length($value, $encoding = null) + { + if ($encoding !== null) { + return mb_strlen($value, $encoding); + } + + return mb_strlen($value); + } + + /** + * Limit the number of characters in a string. + * + * @param string $value + * @param int $limit + * @param string $end + * + * @return string + */ + public static function limit($value, $limit = 100, $end = '...') + { + if (mb_strwidth($value, 'UTF-8') <= $limit) { + return $value; + } + + return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end; + } + + /** + * Convert the given string to lower-case. + * + * @param string $value + * + * @return string + */ + public static function lower($value) + { + return mb_strtolower($value, 'UTF-8'); + } + + /** + * Limit the number of words in a string. + * + * @param string $value + * @param int $words + * @param string $end + * + * @return string + */ + public static function words($value, $words = 100, $end = '...') + { + preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches); + + if (!isset($matches[0]) || static::length($value) === static::length($matches[0])) { + return $value; + } + + return rtrim($matches[0]).$end; + } + + /** + * Parse a Class. + * + * @param string $callback + * @param string|null $default + * + * @return array + */ + public static function parseCallback($callback, $default = null) + { + return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default]; + } + + /** + * Generate a more truly "random" alpha-numeric string. + * + * @param int $length + * + * @throws Exception + * + * @return string + */ + public static function random($length = 16) + { + $string = ''; + + while (($len = strlen($string)) < $length) { + $size = $length - $len; + + $bytes = function_exists('random_bytes') ? random_bytes($size) : mt_rand(); + + $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); + } + + return $string; + } + + /** + * Replace a given value in the string sequentially with an array. + * + * @param string $search + * @param array $replace + * @param string $subject + * + * @return string + */ + public static function replaceArray($search, array $replace, $subject) + { + foreach ($replace as $value) { + $subject = static::replaceFirst($search, $value, $subject); + } + + return $subject; + } + + /** + * Replace the first occurrence of a given value in the string. + * + * @param string $search + * @param string $replace + * @param string $subject + * + * @return string + */ + public static function replaceFirst($search, $replace, $subject) + { + if ($search == '') { + return $subject; + } + + $position = strpos($subject, $search); + + if ($position !== false) { + return substr_replace($subject, $replace, $position, strlen($search)); + } + + return $subject; + } + + /** + * Replace the last occurrence of a given value in the string. + * + * @param string $search + * @param string $replace + * @param string $subject + * + * @return string + */ + public static function replaceLast($search, $replace, $subject) + { + $position = strrpos($subject, $search); + + if ($position !== false) { + return substr_replace($subject, $replace, $position, strlen($search)); + } + + return $subject; + } + + /** + * Begin a string with a single instance of a given value. + * + * @param string $value + * @param string $prefix + * + * @return string + */ + public static function start($value, $prefix) + { + $quoted = preg_quote($prefix, '/'); + + return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value); + } + + /** + * Convert the given string to upper-case. + * + * @param string $value + * + * @return string + */ + public static function upper($value) + { + return mb_strtoupper($value, 'UTF-8'); + } + + /** + * Convert the given string to title case. + * + * @param string $value + * + * @return string + */ + public static function title($value) + { + return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); + } + + /** + * Generate a URL friendly "slug" from a given string. + * + * @param string $title + * @param string $separator + * @param string $language + * + * @return string + */ + public static function slug($title, $separator = '-', $language = 'en') + { + $title = static::ascii($title, $language); + + // Convert all dashes/underscores into separator + $flip = $separator == '-' ? '_' : '-'; + + $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title); + + // Replace @ with the word 'at' + $title = str_replace('@', $separator.'at'.$separator, $title); + + // Remove all characters that are not the separator, letters, numbers, or whitespace. + $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title)); + + // Replace all separator characters and whitespace by a single separator + $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); + + return trim($title, $separator); + } + + /** + * Convert a string to snake case. + * + * @param string $value + * @param string $delimiter + * + * @return string + */ + public static function snake($value, $delimiter = '_') + { + $key = $value; + + if (isset(static::$snakeCache[$key][$delimiter])) { + return static::$snakeCache[$key][$delimiter]; + } + + if (!ctype_lower($value)) { + $value = preg_replace('/\s+/u', '', ucwords($value)); + + $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value)); + } + + return static::$snakeCache[$key][$delimiter] = $value; + } + + /** + * Determine if a given string starts with a given substring. + * + * @param string $haystack + * @param string|array $needles + * + * @return bool + */ + public static function startsWith($haystack, $needles) + { + foreach ((array) $needles as $needle) { + if ($needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) { + return true; + } + } + + return false; + } + + /** + * Convert a value to studly caps case. + * + * @param string $value + * + * @return string + */ + public static function studly($value) + { + $key = $value; + + if (isset(static::$studlyCache[$key])) { + return static::$studlyCache[$key]; + } + + $value = ucwords(str_replace(['-', '_'], ' ', $value)); + + return static::$studlyCache[$key] = str_replace(' ', '', $value); + } + + /** + * Returns the portion of string specified by the start and length parameters. + * + * @param string $string + * @param int $start + * @param int|null $length + * + * @return string + */ + public static function substr($string, $start, $length = null) + { + return mb_substr($string, $start, $length, 'UTF-8'); + } + + /** + * Make a string's first character uppercase. + * + * @param string $string + * + * @return string + */ + public static function ucfirst($string) + { + return static::upper(static::substr($string, 0, 1)).static::substr($string, 1); + } + + /** + * Convert string's encoding. + * + * @author yansongda + * + * @param string $string + * @param string $to + * @param string $from + * + * @return string + */ + public static function encoding($string, $to = 'utf-8', $from = 'gb2312') + { + return mb_convert_encoding($string, $to, $from); + } + + /** + * Returns the replacements for the ascii method. + * + * Note: Adapted from Stringy\Stringy. + * + * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt + * + * @return array + */ + protected static function charsArray() + { + static $charsArray; + + if (isset($charsArray)) { + return $charsArray; + } + + return $charsArray = [ + '0' => ['°', '₀', '۰', '0'], + '1' => ['¹', '₁', '۱', '1'], + '2' => ['²', '₂', '۲', '2'], + '3' => ['³', '₃', '۳', '3'], + '4' => ['⁴', '₄', '۴', '٤', '4'], + '5' => ['⁵', '₅', '۵', '٥', '5'], + '6' => ['⁶', '₆', '۶', '٦', '6'], + '7' => ['⁷', '₇', '۷', '7'], + '8' => ['⁸', '₈', '۸', '8'], + '9' => ['⁹', '₉', '۹', '9'], + 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'], + 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], + 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], + 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], + 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'], + 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], + 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'], + 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], + 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i'], + 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], + 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'], + 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'], + 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], + 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'], + 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'], + 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], + 'q' => ['ყ', 'q'], + 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], + 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'], + 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'], + 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'], + 'v' => ['в', 'ვ', 'ϐ', 'v'], + 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], + 'x' => ['χ', 'ξ', 'x'], + 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], + 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], + 'aa' => ['ع', 'आ', 'آ'], + 'ae' => ['æ', 'ǽ'], + 'ai' => ['ऐ'], + 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], + 'dj' => ['ђ', 'đ'], + 'dz' => ['џ', 'ძ'], + 'ei' => ['ऍ'], + 'gh' => ['غ', 'ღ'], + 'ii' => ['ई'], + 'ij' => ['ij'], + 'kh' => ['х', 'خ', 'ხ'], + 'lj' => ['љ'], + 'nj' => ['њ'], + 'oe' => ['ö', 'œ', 'ؤ'], + 'oi' => ['ऑ'], + 'oii' => ['ऒ'], + 'ps' => ['ψ'], + 'sh' => ['ш', 'შ', 'ش'], + 'shch' => ['щ'], + 'ss' => ['ß'], + 'sx' => ['ŝ'], + 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], + 'ts' => ['ц', 'ც', 'წ'], + 'ue' => ['ü'], + 'uu' => ['ऊ'], + 'ya' => ['я'], + 'yu' => ['ю'], + 'zh' => ['ж', 'ჟ', 'ژ'], + '(c)' => ['©'], + 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'], + 'B' => ['Б', 'Β', 'ब', 'B'], + 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'], + 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'], + 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'], + 'F' => ['Ф', 'Φ', 'F'], + 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], + 'H' => ['Η', 'Ή', 'Ħ', 'H'], + 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'], + 'J' => ['J'], + 'K' => ['К', 'Κ', 'K'], + 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], + 'M' => ['М', 'Μ', 'M'], + 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], + 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'], + 'P' => ['П', 'Π', 'P'], + 'Q' => ['Q'], + 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], + 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], + 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], + 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'], + 'V' => ['В', 'V'], + 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], + 'X' => ['Χ', 'Ξ', 'X'], + 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], + 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], + 'AE' => ['Æ', 'Ǽ'], + 'Ch' => ['Ч'], + 'Dj' => ['Ђ'], + 'Dz' => ['Џ'], + 'Gx' => ['Ĝ'], + 'Hx' => ['Ĥ'], + 'Ij' => ['IJ'], + 'Jx' => ['Ĵ'], + 'Kh' => ['Х'], + 'Lj' => ['Љ'], + 'Nj' => ['Њ'], + 'Oe' => ['Œ'], + 'Ps' => ['Ψ'], + 'Sh' => ['Ш'], + 'Shch' => ['Щ'], + 'Ss' => ['ẞ'], + 'Th' => ['Þ'], + 'Ts' => ['Ц'], + 'Ya' => ['Я'], + 'Yu' => ['Ю'], + 'Zh' => ['Ж'], + ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"], + ]; + } + + /** + * Returns the language specific replacements for the ascii method. + * + * Note: Adapted from Stringy\Stringy. + * + * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt + * + * @param string $language + * + * @return array|null + */ + protected static function languageSpecificCharsArray($language) + { + static $languageSpecific; + if (!isset($languageSpecific)) { + $languageSpecific = [ + 'bg' => [ + ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], + ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'], + ], + 'de' => [ + ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'], + ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], + ], + ]; + } + + return isset($languageSpecific[$language]) ? $languageSpecific[$language] : null; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/HasHttpRequest.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/HasHttpRequest.php new file mode 100644 index 0000000..0e13c3e --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/HasHttpRequest.php @@ -0,0 +1,168 @@ + + * + * @param string $endpoint + * @param array $query + * @param array $headers + * + * @return array|string + */ + public function get($endpoint, $query = [], $headers = []) + { + return $this->request('get', $endpoint, [ + 'headers' => $headers, + 'query' => $query, + ]); + } + + /** + * Send a POST request. + * + * @author yansongda + * + * @param string $endpoint + * @param string|array $data + * @param array $options + * + * @return array|string + */ + public function post($endpoint, $data, $options = []) + { + if (!is_array($data)) { + $options['body'] = $data; + } else { + $options['form_params'] = $data; + } + + return $this->request('post', $endpoint, $options); + } + + /** + * Send request. + * + * @author yansongda + * + * @param string $method + * @param string $endpoint + * @param array $options + * + * @return array|string + */ + public function request($method, $endpoint, $options = []) + { + return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options)); + } + + /** + * Set http client. + * + * @author yansongda + * + * @param Client $client + * + * @return $this + */ + public function setHttpClient(Client $client) + { + $this->httpClient = $client; + + return $this; + } + + /** + * Get default options. + * + * @author yansongda + * + * @return array + */ + public function getOptions() + { + return array_merge([ + 'base_uri' => property_exists($this, 'baseUri') ? $this->baseUri : '', + 'timeout' => property_exists($this, 'timeout') ? $this->timeout : 5.0, + 'connect_timeout' => property_exists($this, 'connectTimeout') ? $this->connectTimeout : 5.0, + ], $this->httpOptions); + } + + /** + * Return http client. + * + * @return Client + */ + public function getHttpClient() + { + if (is_null($this->httpClient)) { + $this->httpClient = $this->getDefaultHttpClient(); + } + + return $this->httpClient; + } + + /** + * Get default http client. + * + * @author yansongda + * + * @return Client + */ + public function getDefaultHttpClient() + { + return new Client($this->getOptions()); + } + + /** + * Convert response. + * + * @author yansongda + * + * @param ResponseInterface $response + * + * @return array|string + */ + public function unwrapResponse(ResponseInterface $response) + { + $contentType = $response->getHeaderLine('Content-Type'); + $contents = $response->getBody()->getContents(); + + if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) { + return json_decode($contents, true); + } elseif (false !== stripos($contentType, 'xml')) { + return json_decode(json_encode(simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + return $contents; + } +} diff --git a/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/ShouldThrottle.php b/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/ShouldThrottle.php new file mode 100644 index 0000000..50893b6 --- /dev/null +++ b/addons/weliam_smartcity/vendor/yansongda/supports/src/Traits/ShouldThrottle.php @@ -0,0 +1,149 @@ + 60, + 'period' => 60, + 'count' => 0, + 'reset_time' => 0 + ]; + + /** + * isThrottled. + * + * @author yansongda + * + * @param string $key + * @param int $limit + * @param int $period + * @param bool $auto_add + * + * @return bool + */ + public function isThrottled($key, $limit = 60, $period = 60, $auto_add = false) + { + if ($limit === -1) { + return false; + } + + $now = microtime(true) * 1000; + + $this->redis->zremrangebyscore($key, 0, $now - $period * 1000); + + $this->_throttle = [ + 'limit' => $limit, + 'period' => $period, + 'count' => $this->getThrottleCounts($key, $period), + 'reset_time' => $this->getThrottleResetTime($key, $now), + ]; + + if ($this->_throttle['count'] < $limit) { + if ($auto_add) { + $this->throttleAdd($key, $period); + } + + return false; + } + + return true; + } + + /** + * 限流 + 1. + * + * @author yansongda + * + * @param string $key + * @param int $period + * + * @return void + */ + public function throttleAdd($key, $period = 60) + { + $now = microtime(true) * 1000; + + $this->redis->zadd($key, [$now => $now]); + $this->redis->expire($key, $period * 2); + } + + /** + * getResetTime. + * + * @author yansongda + * + * @param $key + * @param $now + * + * @return int + */ + public function getThrottleResetTime($key, $now) + { + $data = $this->redis->zrangebyscore( + $key, + $now - $this->_throttle['period'] * 1000, + $now, + ['limit' => [0, 1]] + ); + + if (count($data) === 0) { + return $this->_throttle['reset_time'] = time() + $this->_throttle['period']; + } + + return intval($data[0] / 1000) + $this->_throttle['period']; + } + + /** + * 获取限流相关信息. + * + * @author yansongda + * + * @param null $key + * @param null $default + * + * @return array|null + */ + public function getThrottleInfo($key = null, $default = null) + { + if (is_null($key)) { + return $this->_throttle; + } + + if (isset($this->_throttle[$key])) { + return $this->_throttle[$key]; + } + + return $default; + } + + /** + * 获取已使用次数. + * + * @author yansongda + * + * @param $key + * @param int $period + * + * @return string + */ + public function getThrottleCounts($key, $period = 60) + { + $now = microtime(true) * 1000; + + return $this->redis->zcount($key, $now - $period * 1000, $now); + } +}