├── .gitignore ├── LICENSE.996ICU.en.txt ├── LICENSE.996ICU.txt ├── bin └── fly ├── composer.json ├── config ├── laravelfly-app-config.example.php ├── laravelfly-server-config.example.php ├── nginx+swoole.conf ├── package_config_examples │ ├── AsgardCms.md │ └── laravel-localization.md ├── swoole_fallback_to_phpfpm.conf └── use_swoole_or_fpm_depending_on_clients.conf ├── doc ├── dev.md └── tinker.md ├── docker ├── Dockerfile └── docker-compose.yml ├── phpunit.xml.dist ├── readme.md ├── src ├── LaravelFly │ ├── ApplicationTrait │ │ ├── InConsole.php │ │ ├── NoMorePackageManifest.php │ │ ├── ProvidersInRequest.php │ │ └── Server.php │ ├── Backup │ │ ├── Application.php │ │ ├── Bootstrap │ │ │ ├── BackupAttributes.php │ │ │ ├── BackupConfigs.php │ │ │ ├── LoadConfiguration.php │ │ │ └── SetBackupForBaseServices.php │ │ ├── Kernel.php │ │ ├── ProviderRepository.php │ │ └── ProviderRepositoryInRequest.php │ ├── Command.php │ ├── Exception │ │ └── LaravelFlyException.php │ ├── Fly.php │ ├── FpmLike │ │ └── Application.php │ ├── FrontEnd │ │ ├── Controllers │ │ │ ├── BaseController.php │ │ │ └── InfoController.php │ │ └── views │ │ │ ├── info │ │ │ ├── index.blade.php │ │ │ ├── table-table.blade.php │ │ │ └── table.blade.php │ │ │ ├── layouts │ │ │ └── info.blade.php │ │ │ └── partials │ │ │ ├── grid.blade.php │ │ │ └── table.blade.php │ ├── Kernel.php │ ├── Map │ │ ├── Application.php │ │ ├── Bootstrap │ │ │ ├── CleanOnWorker.php │ │ │ ├── LoadConfiguration.php │ │ │ ├── OnWork.php │ │ │ ├── RegisterAcrossProviders.php │ │ │ └── ResolveSomeFacadeAliases.php │ │ ├── CommonHack │ │ │ ├── Kernel.php │ │ │ ├── RouterMiddleware.php │ │ │ └── StreamHandlerLogCache.php │ │ ├── Illuminate │ │ │ ├── Auth │ │ │ │ ├── AuthManager.php │ │ │ │ ├── AuthManagerSame.php │ │ │ │ ├── AuthServiceProvider.php │ │ │ │ └── Gate.php │ │ │ ├── Cookie │ │ │ │ ├── CookieJar.php │ │ │ │ ├── CookieJarSame.php │ │ │ │ └── CookieServiceProvider.php │ │ │ ├── Database │ │ │ │ ├── Connection │ │ │ │ │ ├── FakeSwooleConnTrait.php │ │ │ │ │ └── SwooleMySQLConnection.php │ │ │ │ ├── ConnectionFactory.php │ │ │ │ ├── ConnectionsTrait.php │ │ │ │ ├── Connectors │ │ │ │ │ ├── MySQLConnectorTrait.php │ │ │ │ │ ├── MySQLLaravelConnector.php │ │ │ │ │ ├── MySQLSmfConnector.php │ │ │ │ │ ├── RedisConnectorTrait.php │ │ │ │ │ └── RedisSmfConnector.php │ │ │ │ ├── DatabaseManager.php │ │ │ │ ├── DatabaseServiceProvider.php │ │ │ │ ├── PDO │ │ │ │ │ ├── ConnectionException.php │ │ │ │ │ ├── StatementException.php │ │ │ │ │ ├── SwoolePDO.php │ │ │ │ │ └── SwoolePDOStatement.php │ │ │ │ └── Pool │ │ │ │ │ ├── Pool.php │ │ │ │ │ ├── SimplePool.php │ │ │ │ │ └── SmfPool.php │ │ │ ├── Redis │ │ │ │ ├── Connection │ │ │ │ │ ├── EnsureConnected.php │ │ │ │ │ ├── PhpRedisConnection.php │ │ │ │ │ ├── PredisConnection.php │ │ │ │ │ ├── SwooleRedisConnection.php │ │ │ │ │ └── SwooleRedisNot.php │ │ │ │ ├── Connector │ │ │ │ │ ├── PhpRedisConnector.php │ │ │ │ │ ├── PredisConnector.php │ │ │ │ │ └── SwooleRedisConnector.php │ │ │ │ ├── RedisManager.php │ │ │ │ └── RedisServiceProvider.php │ │ │ ├── Session │ │ │ │ ├── CookieSessionHandler.php │ │ │ │ ├── DatabaseSessionHandler.php │ │ │ │ ├── SessionManager.php │ │ │ │ ├── SessionServiceProvider.php │ │ │ │ ├── StartSession.php │ │ │ │ └── Swoole │ │ │ │ │ └── SwooleSessionHandler.php │ │ │ ├── Translation │ │ │ │ ├── FileLoader.php │ │ │ │ ├── TranslationServiceProvider.php │ │ │ │ └── Translator.php │ │ │ └── View │ │ │ │ ├── BladeCompiler_1.php │ │ │ │ ├── Factory.php │ │ │ │ └── ViewServiceProvider.php │ │ ├── IlluminateBase │ │ │ ├── Dispatcher.php │ │ │ ├── EventServiceProvider.php │ │ │ ├── Request.php │ │ │ └── SingletonRequestException.php │ │ ├── Kernel.php │ │ └── Util │ │ │ ├── Dict.php │ │ │ └── StaticDict.php │ ├── Providers │ │ ├── ConfigCacheCommand.php │ │ ├── ConfigClearCommand.php │ │ ├── RouteServiceProvider.php │ │ └── ServiceProvider.php │ ├── Server │ │ ├── Common.php │ │ ├── FpmHttpServer.php │ │ ├── HttpServer.php │ │ ├── ServerInterface.php │ │ ├── Traits │ │ │ ├── Console.php │ │ │ ├── DispatchRequestByQuery.php │ │ │ ├── Laravel.php │ │ │ ├── Preloader.php │ │ │ ├── ShareInteger.php │ │ │ ├── ShareTable.php │ │ │ ├── Task.php │ │ │ ├── Tinker.php │ │ │ ├── Worker.php │ │ │ ├── preloader_config_more.php │ │ │ └── preloader_config_onlymapmode.php │ │ └── WebSocketServer.php │ ├── Tinker │ │ ├── ClassAliasAutoloader.php │ │ ├── Shell.php │ │ ├── TinkerCaster.php │ │ ├── TinkerCommand.php │ │ └── WhereamiCommand.php │ ├── Tools │ │ ├── LaravelJobByTask │ │ │ ├── Connectors │ │ │ │ └── TaskJobConnector.php │ │ │ ├── TaskJob.php │ │ │ ├── TaskJobQueue.php │ │ │ ├── TaskJobServiceProvider.php │ │ │ └── Worker.php │ │ ├── SessionTable.php │ │ ├── Table.php │ │ └── TablePipe │ │ │ ├── Pipe.php │ │ │ ├── PipeInterface.php │ │ │ ├── PlainFilePipe.php │ │ │ └── RedisPipe.php │ └── WebSocketHandler │ │ └── WebSocketHandlerInterface.php ├── MidKernel.php ├── fly │ └── Kernel.php └── functions.php └── tests ├── Backup └── Unit │ └── PropsTest.php ├── BaseTestCase.php ├── DirTest.php ├── Map ├── Feature │ ├── ExtendedFlyFilesTest.php │ ├── Fly │ │ └── StreamHandlerTest.php │ ├── FlyOfficialFilesTest.php │ ├── ObjectsInWorkerTest.php │ └── SuperGlobalVarsTest.php ├── LaravelTests │ ├── TestCase.php │ └── class_replace_sed.txt └── Unit │ ├── ApplicationCorTest.php │ ├── FlyTest.php │ ├── Illuminate │ └── Translation │ │ └── TranslatorTest.php │ └── Server │ ├── CommonTest.php │ ├── HttpServerTest.php │ ├── IncludeFlyFilesTest.php │ └── Traits │ ├── DispatchRequestByQueryTest.php │ └── WorkerTest.php ├── bootstrap.php ├── offcial_files └── Kernel.php └── swoole_src_tests ├── include ├── bootstrap.php ├── config.php ├── lib │ ├── class.websocket_client.php │ ├── curl.php │ └── curl_concurrency.php ├── swoole.inc └── toolkit │ ├── RandStr.php │ ├── TcpStat.php │ └── functions.php └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.git 2 | /vendor 3 | /.idea 4 | /.vscode -------------------------------------------------------------------------------- /LICENSE.996ICU.en.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2 | 3 | Anti 996 License Version 1.0 (Draft) 4 | 5 | Permission is hereby granted to any individual or legal entity 6 | obtaining a copy of this licensed work (including the source code, 7 | documentation and/or related items, hereinafter collectively referred 8 | to as the "licensed work"), free of charge, to deal with the licensed 9 | work for any purpose, including without limitation, the rights to use, 10 | reproduce, modify, prepare derivative works of, distribute, publish 11 | and sublicense the licensed work, subject to the following conditions: 12 | 13 | 1. The individual or the legal entity must conspicuously display, 14 | without modification, this License and the notice on each redistributed 15 | or derivative copy of the Licensed Work. 16 | 17 | 2. The individual or the legal entity must strictly comply with all 18 | applicable laws, regulations, rules and standards of the jurisdiction 19 | relating to labor and employment where the individual is physically 20 | located or where the individual was born or naturalized; or where the 21 | legal entity is registered or is operating (whichever is stricter). In 22 | case that the jurisdiction has no such laws, regulations, rules and 23 | standards or its laws, regulations, rules and standards are 24 | unenforceable, the individual or the legal entity are required to 25 | comply with Core International Labor Standards. 26 | 27 | 3. The individual or the legal entity shall not induce or force its 28 | employee(s), whether full-time or part-time, or its independent 29 | contractor(s), in any methods, to agree in oral or written form, to 30 | directly or indirectly restrict, weaken or relinquish his or her 31 | rights or remedies under such laws, regulations, rules and standards 32 | relating to labor and employment as mentioned above, no matter whether 33 | such written or oral agreement are enforceable under the laws of the 34 | said jurisdiction, nor shall such individual or the legal entity 35 | limit, in any methods, the rights of its employee(s) or independent 36 | contractor(s) from reporting or complaining to the copyright holder or 37 | relevant authorities monitoring the compliance of the license about 38 | its violation(s) of the said license. 39 | 40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 47 | -------------------------------------------------------------------------------- /LICENSE.996ICU.txt: -------------------------------------------------------------------------------- 1 | 版权所有(c)<年份><版权持有人> 2 | 3 | 反996许可证版本1.0 4 | 5 | 在符合下列条件的情况下,特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以下 6 | 统称为“授权作品”)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括但 7 | 不限于使用、复制,修改,衍生利用、散布,发布和再许可: 8 | 9 | 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不得自行修 10 | 改。 11 | 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或经营地 12 | (以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和标准。如果该司法管辖 13 | 区没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可执行,则个人或法人实体必须遵守国 14 | 际劳工标准的核心公约。 15 | 3. 个人或法人不得以任何方式诱导或强迫其全职或兼职员工或其独立承包人以口头或书面形式同意直接或 16 | 间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和标准保护的权利或补救 17 | 措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该等个人或法人实体也不得以任何方法 18 | 限制其雇员或独立承包人向版权持有人或监督许可证合规情况的有关当局报告或投诉上述违反许可证的行为 19 | 的权利。 20 | 21 | 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵 22 | 权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均不承担因本软件或 23 | 本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。 24 | -------------------------------------------------------------------------------- /bin/fly: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | exe(); return; 7 | 8 | if ($argc == 1) { 9 | $msg = <</fly.conf.php. 13 | is the root of the project which LaravelFly is installed. 14 | 15 | U; 16 | die($msg); 17 | } 18 | 19 | // drop __DIR__ to avoid the situation that laravel-fly is at a symlink dir 20 | // $basePath = realpath(__DIR__ . '/../../../..'); 21 | $basePath= dirname($_SERVER["SCRIPT_FILENAME"],5); 22 | 23 | $config_file = $argc == 3 ? $argv[2] : $basePath . '/fly.conf.php'; 24 | 25 | try { 26 | if (!is_file($config_file)) { 27 | $config_file = dirname(__DIR__) . '/config/laravelfly-server-config.example.php'; 28 | } 29 | $options = require $config_file; 30 | $options['conf'] = $config_file; 31 | } catch (Exception $e) { 32 | exit("config file not be loaded: $config_file"); 33 | } 34 | 35 | if ($options['echo_level'] ?? 0 >= 3) 36 | echo "[INFO] {$argv[1]} $config_file\n"; 37 | 38 | if ($argv[1] == 'start') { 39 | goto start; 40 | } 41 | 42 | if (!isset($options['pid_file'])) { 43 | $pid_file = $basePath . '/bootstrap/laravel-fly-' . $options['listen_port'] . '.pid'; 44 | } else { 45 | $pid_file = $options['pid_file'] . '-' . $options['listen_port']; 46 | } 47 | 48 | $pid = 0; 49 | try { 50 | if (is_file($pid_file)) 51 | $pid = (int)file_get_contents($pid_file); 52 | } catch (Throwable $e) { 53 | print("pid can not be read from $pid_file \n"); 54 | } 55 | 56 | if (!$pid && $argv[1] != 'stop') { 57 | goto start; 58 | } 59 | 60 | switch ($argv[1]) { 61 | case 'stop': 62 | posix_kill($pid, SIGTERM); 63 | break; 64 | case 'reload': 65 | posix_kill($pid, SIGUSR1); 66 | break; 67 | case 'restart': 68 | posix_kill($pid, SIGTERM); 69 | goto start; 70 | break; 71 | } 72 | 73 | 74 | exit(); 75 | 76 | start: 77 | include $basePath . '/vendor/autoload.php'; 78 | 79 | // prevent errors if eval(tinker()); left in project code 80 | if (empty($options['tinker']) && !function_exists('tinker')) { 81 | function tinker() 82 | { 83 | } 84 | } 85 | 86 | unset($basePath, $config_file, $pid_file, $pid); 87 | \LaravelFly\Fly::getServer($options)->start(); 88 | 89 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scil/laravel-fly", 3 | "description": "make laravel faster by using swoole extension.", 4 | "type": "library", 5 | "keywords": [ 6 | "swoole", 7 | "laravel" 8 | ], 9 | "homepage": "http://www.github.com/scil/laravel_fly", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "scil" 14 | } 15 | ], 16 | "require": { 17 | "scil/laravel-fly-files":"5.5.* || 5.6.* || 5.7.* || 5.8.*|| 6.0.* ", 18 | "laravel/framework": "5.5.* || 5.6.* || 5.7.* || 5.8.* || 6.0.*", 19 | "hhxsv5/laravel-s": "~2.0", 20 | "open-smf/connection-pool": "~1.0", 21 | "laravel/helpers": "*", 22 | "ext-swoole": ">=4.2.13", 23 | "composer/composer": "^1.8" 24 | }, 25 | "suggest": { 26 | "ext-inotify": ">=2.0.0" 27 | }, 28 | "require-dev": { 29 | "shixinke/php-ide-helper": "dev-master", 30 | "nunomaduro/collision": "^2.0", 31 | "phpunit/phpunit": "^7.0", 32 | "mockery/mockery": "^1.0", 33 | "orchestra/testbench-core": "^3.6" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "LaravelFly\\": "src/LaravelFly/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "LaravelFly\\Tests\\": "tests/" 43 | } 44 | }, 45 | "extra": { 46 | "laravel": { 47 | "providers": [ 48 | "LaravelFly\\Providers\\ServiceProvider" 49 | ], 50 | "aliases": { 51 | }, 52 | "dont-discover": [ 53 | "hhxsv5/laravel-s" 54 | ] 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /config/nginx+swoole.conf: -------------------------------------------------------------------------------- 1 | # warn: by default this file is used by docker, be careful when editing it 2 | 3 | 4 | user www-data www-data; 5 | 6 | worker_processes 2; 7 | 8 | pid /var/run/nginx.pid; 9 | 10 | worker_rlimit_nofile 1024; 11 | 12 | # include /etc/nginx/modules-enabled/*.conf; 13 | 14 | events { 15 | worker_connections 512; 16 | } 17 | 18 | http { 19 | 20 | include /etc/nginx/mime.types; 21 | default_type application/octet-stream; 22 | sendfile off; 23 | tcp_nopush on; 24 | access_log /var/log/nginx/access.log; 25 | error_log /var/log/nginx/error.log warn; 26 | 27 | 28 | #include /etc/nginx/conf.d/*.conf; 29 | #include /etc/nginx/sites-enabled/*; 30 | 31 | 32 | 33 | 34 | server { 35 | listen 80; 36 | root /usr/share/nginx/html; 37 | charset utf-8; 38 | index index.html; 39 | 40 | # for firefox, otherwise firefox may dowload some page; default file type is: octet-stream 41 | default_type text/html; 42 | 43 | location = / { 44 | # if you have a static home page , try this one: 45 | # try_files index.html @other; 46 | try_files '' @other; 47 | } 48 | location / { 49 | try_files $uri $uri/ @other; 50 | } 51 | 52 | location @other { 53 | proxy_http_version 1.1; 54 | proxy_set_header Connection "keep-alive"; 55 | 56 | # proxy_connect_timeout 60s; 57 | # proxy_send_timeout 60s; 58 | # proxy_read_timeout 120s; 59 | 60 | # swoole is the name in ../docker/docker-compose.yml for php service 61 | # it can be changed to 127.0.0.1 if this conf is used by a nginx on same machine 62 | proxy_pass http://swoole:9501; 63 | proxy_set_header X-Real-IP $remote_addr; 64 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 65 | proxy_set_header Host $http_host; 66 | 67 | # proxy_set_header X-Real-PORT $remote_port; 68 | # proxy_set_header Scheme $scheme; 69 | # proxy_set_header Server-Protocol $server_protocol; 70 | # proxy_set_header Server-Name $server_name; 71 | # proxy_set_header Server-Addr $server_addr; 72 | # proxy_set_header Server-Port $server_port; 73 | 74 | } 75 | 76 | # # only for Let's Encrypt 77 | # location ~ /.well-known { 78 | # allow all; 79 | # # set root is necessage, otherwise "Invalid response from domain..." 80 | # # https://www.digitalocean.com/community/questions/letsencrypt-problem-renewing-certs-invalid-response-from-domain 81 | # root {{ doc_root }}; 82 | # } 83 | 84 | } 85 | 86 | 87 | } -------------------------------------------------------------------------------- /config/package_config_examples/AsgardCms.md: -------------------------------------------------------------------------------- 1 | 2 | [AsgardCms](https://github.com/AsgardCms/Platform) is a modular multilingual CMS built with Laravel 5 3 | 4 | ## install AsgardCms 5 | https://asgardcms.com/install 6 | 7 | ## config/laravelfly.php 8 | 1 add its ServiceProvider name to the `providers_in_request` array 9 | ``` 10 | Modules\Core\Providers\AsgardServiceProvider::class, 11 | ``` 12 | 13 | ## hack 14 | edit asgard/app/Providers/AppServiceProvider.php 15 | 16 | comment these lines to stop AsgardCms enable Debugbar: 17 | ``` 18 | if ($this->app->environment() == 'local') { 19 | $this->app->register('Barryvdh\Debugbar\ServiceProvider'); 20 | } 21 | ``` 22 | 23 | ## Final 24 | All of AsgardCms services are hard to registered or booted on WorkerStart. All of them are registered and booted in each request. So AsgardCms can't use LaravelFly power. I use AsgardCms to make sure Laravel works fine on LaravelFly. 25 | 26 | 27 | -------------------------------------------------------------------------------- /config/swoole_fallback_to_phpfpm.conf: -------------------------------------------------------------------------------- 1 | 2 | upstream laravelfly_group { 3 | server 127.0.0.1:9501 weight=1 max_fails=1 fail_timeout=10 ; 4 | server 127.0.0.1:81 backup; 5 | 6 | # The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed. 7 | # It does not limit the total number of connections to upstream servers that an nginx worker process can open. The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well. 8 | # https://gist.github.com/magnetikonline/4a2e68b2ce94bd0c945e 9 | # https://ma.ttias.be/enable-keepalive-connections-in-nginx-upstream-proxy-configurations/ 10 | keepalive 8; 11 | } 12 | log_format my_upstream '$remote_addr|$time_local|$request_time|$upstream_response_time|$status ' 13 | '$upstream_addr|$request'; 14 | 15 | server { 16 | server_name fly.test; 17 | listen 80; 18 | root /www/fly/public; 19 | index index.html; 20 | charset utf-8; 21 | 22 | location = / { 23 | # if you have a static home page , try this one: 24 | # try_files index.html @other; 25 | try_files '' @php; 26 | } 27 | 28 | location / { 29 | try_files $uri $uri/ @php; 30 | } 31 | 32 | location @php { 33 | proxy_pass http://laravelfly_group; 34 | 35 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 36 | proxy_http_version 1.1; 37 | # Remove the Connection header if the client sends it, 38 | # it could be "close" to close a keepalive connection 39 | proxy_set_header Connection ""; 40 | 41 | # proxy_connect_timeout 60s; 42 | # proxy_send_timeout 60s; 43 | # proxy_read_timeout 120s; 44 | 45 | proxy_set_header X-Real-IP $remote_addr; 46 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 47 | proxy_set_header Host $http_host; 48 | 49 | # proxy_set_header X-Real-PORT $remote_port; 50 | # proxy_set_header Scheme $scheme; 51 | # proxy_set_header Server-Protocol $server_protocol; 52 | # proxy_set_header Server-Name $server_name; 53 | # proxy_set_header Server-Addr $server_addr; 54 | # proxy_set_header Server-Port $server_port; 55 | 56 | # just a mark for different upstream server, you can disable it 57 | add_header X-Upstream $upstream_addr; 58 | # log 59 | access_log /var/log/nginx/upstream.log my_upstream; 60 | 61 | } 62 | 63 | # # only for Let's Encrypt 64 | # location ~ /.well-known { 65 | # allow all; 66 | # # set root is necessage, otherwise "Invalid response from domain..." 67 | # # https://www.digitalocean.com/community/questions/letsencrypt-problem-renewing-certs-invalid-response-from-domain 68 | # root {{ doc_root }}; 69 | # } 70 | 71 | 72 | } 73 | 74 | 75 | server { 76 | server_name fly.test; 77 | listen 81; 78 | root /vagrant/www/zc/public; 79 | index index.html index.php; 80 | charset utf-8; 81 | 82 | location / { 83 | try_files '' /index.php?$query_string; 84 | } 85 | 86 | location /index.php { 87 | fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; 88 | fastcgi_index index.php; 89 | include fastcgi.conf; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /doc/dev.md: -------------------------------------------------------------------------------- 1 | ## Dev starter 2 | 3 | 1. git clone https://github.com/scil/LaravelFly.git 4 | 5 | 2. add following to the composer.json of a project with Laravel 6 | ``` 7 | "repositories": [ 8 | { 9 | "type": "path", 10 | "url": "" 11 | } 12 | ] 13 | ``` 14 | 15 | 3. `cd && composer install --prefer-source ` . 16 | If the project is in a VirtualBox shared dir, it may failed to symlinking to , the solution is https://www.virtualbox.org/ticket/10085#comment:32 17 | `--prefer-source` to load laravel/framework/tests ( [How to download excluded paths via composer?](https://stackoverflow.com/questions/28169938/how-to-download-excluded-paths-via-composer) ) 18 | 19 | ## test with phpunit 20 | 21 | 1. set this env var in /phpunit.xml.dist 22 | ``` 23 | 24 | ``` 25 | 26 | 2. see comments in tests/BaseTestCase.php 27 | 28 | ## Bridge between Laravel and LaravelFly 29 | 30 | - overwrite artisan command 'config.cache' which could load laravelfly.php in config dir. code: LaravelFly\Providers\ConfigCacheCommand 31 | 32 | ## Update [laravel-fly-files](https://github.com/scil/LaravelFly-fly-files) for updated minor version of Laravel 33 | 34 | ( If env is ready-made, go to step 3 and 4.) 35 | 36 | e.g. updating_dir Laravel 5.7.28 37 | 38 | 1. create a new project 39 | 40 | ``` 41 | mkdir updating_dir 42 | cd updating_dir 43 | vi composer.json 44 | 45 | ``` 46 | 47 | edit 'updating_dir/composer.json' 48 | ``` 49 | "require": { 50 | ... 51 | "laravel/framework": "5.7.*", 52 | "scil/laravel-fly": "dev-master" 53 | }, 54 | "autoload-dev": { 55 | "psr-4": { 56 | ... 57 | "LaravelFly\\Tests\\": "vendor/scil/laravel-fly/tests/" 58 | } 59 | }, 60 | "repositories": [ 61 | { 62 | "type": "path", 63 | "url": "vendor/scil/laravel-fly-files-local" 64 | } 65 | ] 66 | 67 | ``` 68 | 69 | then 70 | ``` 71 | composer install 72 | cp -R vendor/scil/laravel-fly-files vendor/scil/laravel-fly-files-local 73 | ``` 74 | 75 | 2. edit `vendor/scil/laravel-fly-files-local/composer.json` 76 | ``` 77 | "laravel/framework": "5.7.28" 78 | ``` 79 | 80 | 3. edit `vendor/scil/laravel-fly/phpunit.xml.dist` 81 | ```xml 82 | 83 | ``` 84 | 85 | 4. update then run test at laravel project root 86 | ``` 87 | composer update 88 | 89 | phpunit=vendor/bin/phpunit 90 | xml=vendor/scil/laravel-fly/phpunit.xml.dist 91 | 92 | $phpunit --stop-on-failure -c $xml --testsuit only_fly 93 | 94 | ``` 95 | -------------------------------------------------------------------------------- /doc/tinker.md: -------------------------------------------------------------------------------- 1 | # Tinker online 2 | 3 | ## tinker demo 4 | 5 | [![tinker()](https://asciinema.org/a/zq5HDcGf2Fp5HcMtRw0ZOSXXD.png)](https://asciinema.org/a/zq5HDcGf2Fp5HcMtRw0ZOSXXD?t=3) 6 | 7 | 8 | ## use in router 9 | 10 | ```php 11 | Route::get('hi', function () { 12 | $friends = 'Tinker'; 13 | 14 | if (starts_with(Request::ip(), ['192.168', '127'])) { 15 | eval(tinker()); 16 | } 17 | 18 | return view('fly', compact('friends')); 19 | }); 20 | ``` 21 | 22 | ## use in view file 23 | 24 | ```blade.php 25 | @php(eval(tinker())) 26 | 27 | @foreach($friends as $one) 28 | 29 |

Hello, {!! $one !!}!

30 | 31 | @endforeach 32 | ``` 33 | 34 |
35 | The response can be changed to anything by you. 36 |
37 | 38 | ``` 39 | Hello, Tinker! 40 | 41 | Hello, PsySh! 42 | 43 | Hello, World! 44 | ``` 45 | 46 | 47 |
48 |
49 | 50 | 51 | ## tinker abilities 52 | 53 | visit private members, read/write vars, use laravel services and so on. 54 | 55 | tinker use examples. 56 | 57 | ```php 58 | // visit private members 59 | sudo app()->booted 60 | sudo app('router')->routes->actionList 61 | // Mode Map 62 | sudo $view= app()::$corDict[1]['instances']['view'] 63 | 64 | // use Model or Controller without writing namespace, thanks to ClassAliasAutoloader 65 | // and the instance is printed beautifully, thanks to casters provided by laravel 66 | $user = User::first() 67 | 68 | // like dir() in Python 69 | ls -la $user 70 | 71 | // read doc 72 | doc $user->save 73 | 74 | // check source code 75 | show $user->query 76 | 77 | // use xdebug 78 | xdebug_debug_zval('user') 79 | xdebug_debug_zval('url->routes') 80 | xdebug_call_class() 81 | 82 | // magic var 83 | $__file 84 | 85 | // check server pid and pidfile 86 | Fly::getServer() 87 | //same as LaravelFly\Fly::getServer() 88 | 89 | // which class aliases are defined in tinker 90 | sudo app('tinker')->loader->classes 91 | 92 | sudo $middle = Fly::getServer()->kernel->middleware 93 | 94 | // run shell commands 95 | `pwd && ls ` 96 | 97 | ``` 98 | 99 | 100 | ## Tinker Tips 101 | 102 | `eval(tinker())` is a `eval(\Psy\sh())` with extra support for Laravel. It can be used independently without LaravelFly server, but LaravelFly applies the opportunity to use shell online. 103 | 104 | There may be a problem with tabcompletion. see [tabcompletion only works "the second time](https://github.com/bobthecow/psysh/issues/435) 105 | 106 | If you see an error about permission, please try `sudo chmod -R 0777 ~/.config/psysh` 107 | 108 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # swoft/alphp:cli: 2 | # @description php 7.1 image base on the alpine 3.7 3 | # ------------------------------------------------------------------------------------ 4 | # @link https://hub.docker.com/_/alpine/ alpine image 5 | # @link https://hub.docker.com/_/php/ php image 6 | # @link https://github.com/docker-library/php php dockerfiles 7 | # ------------------------------------------------------------------------------------ 8 | # @build-example docker build . -f Dockerfile -t scil/swoole:app1 9 | # 10 | 11 | FROM swoft/alphp:cli 12 | LABEL maintainer="scil" version="1.0" 13 | 14 | ## 15 | # ---------- building ---------- 16 | ## 17 | 18 | RUN set -ex \ 19 | && mkdir -p /var/laravel_app \ 20 | && chown -R www-data:www-data /var/laravel_app \ 21 | && echo -e "\033[42;37m Build Completed :).\033[0m\n" 22 | 23 | EXPOSE 9501 80 24 | VOLUME ["/var/www", "/data"] 25 | WORKDIR /var/www 26 | 27 | ENTRYPOINT ["php", "vendor/scil/laravel-fly/bin/fly", "start"] 28 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.2" 2 | 3 | services: 4 | 5 | swoole: 6 | build: . 7 | container_name: fly-swoole 8 | hostname: swoole 9 | volumes: 10 | - ../../../..:/var/laravel_app 11 | ports: ["9501:9501"] 12 | environment: 13 | #- TIMEZONE=Asia/Hong_Kong 14 | - TIMEZONE=Europe/London 15 | 16 | nginx: 17 | image: nginx 18 | container_name: fly-nginx 19 | volumes: 20 | - ../../../../public:/usr/share/nginx/html 21 | - ../config/nginx+swoole.conf:/etc/nginx/nginx.conf 22 | ports: 23 | - "8080:80" 24 | command: [nginx-debug, '-g', 'daemon off;'] 25 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | ./tests/Map/Unit/ApplicationCorTest.php 15 | 16 | 17 | 18 | 19 | 20 | ./tests/Map/Feature/FlyOfficialFilesTest.php 21 | ./tests/Map/Feature/ExtendedFlyFilesTest.php 22 | 23 | 24 | 25 | ./tests/Map/Unit/FlyTest.php 26 | ./tests/Map/Feature/FlyOfficialFilesTest.php 27 | ./tests/Map/Feature/ObjectsInWorkerTest.php 28 | ./tests/Map/Feature/SuperGlobalVarsTest.php 29 | 30 | ./tests/Map/Feature/Fly/StreamHandlerTest.php 31 | 32 | ./tests/Map/Unit/ApplicationCorTest.php 33 | 34 | ./tests/Map/Unit/Server/IncludeFlyFilesTest.php 35 | ./tests/Map/Unit/Server/CommonTest.php 36 | 37 | ./tests/Map/Unit/Server/HttpServerTest.php 38 | ./tests/Map/Unit/Server/Traits/DispatchRequestByQueryTest.php 39 | ./tests/Map/Unit/Server/Traits/WorkerTest.php 40 | 41 | 42 | ./tests/Map/Unit/Illuminate/Translation/TranslatorTest.php 43 | 44 | 45 | 46 | 47 | ./tests/Map/Unit/Server/CommonTest.php 48 | 49 | 50 | 51 | ./tests/Map/LaravelTests/TestCase.php 52 | 53 | 54 | 55 | ./tests/Backup/Unit/PropsTest.php 56 | 57 | 58 | 59 | 60 | 61 | ./app 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/LaravelFly/ApplicationTrait/InConsole.php: -------------------------------------------------------------------------------- 1 | config['app.providers']) 18 | ->partition(function ($provider) { 19 | return Str::startsWith($provider, 'Illuminate\\'); 20 | }); 21 | 22 | // no more, they have loaded by LaravelFly\Backup\Bootstrap\LoadConfiguration 23 | // or LaravelFly\Map\Bootstrap\LoadConfiguration 24 | // $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); 25 | 26 | (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) 27 | ->load($providers->collapse()->toArray()); 28 | } 29 | } -------------------------------------------------------------------------------- /src/LaravelFly/ApplicationTrait/ProvidersInRequest.php: -------------------------------------------------------------------------------- 1 | getCachedServicesPathInRequest(); 26 | $this->providerRepInRequest = new ProviderRepositoryInRequest($this, new Filesystem, $manifestPath); 27 | $this->providerRepInRequest->makeManifest($providers); 28 | } 29 | } 30 | 31 | public function getCachedServicesPathInRequest() 32 | { 33 | return $this->bootstrapPath() . '/cache/laravelfly_services_in_request.json'; 34 | } 35 | 36 | public function registerConfiguredProvidersInRequest() 37 | { 38 | if ($this->providerRepInRequest) 39 | $this->providerRepInRequest->load([]); 40 | } 41 | 42 | /** 43 | * @param array $services 44 | * @ 45 | */ 46 | public function addDeferredServices(array $services) 47 | { 48 | $this->deferredServices = array_merge($this->deferredServices, $services); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LaravelFly/ApplicationTrait/Server.php: -------------------------------------------------------------------------------- 1 | server; 25 | } 26 | 27 | public function getSwooleServer(): \swoole_server 28 | { 29 | return $this->server->swoole; 30 | } 31 | 32 | /** 33 | * @param \LaravelFly\Server\ServerInterface|\LaravelFly\Server\FpmHttpServer|\LaravelFly\Server\HttpServer $server 34 | */ 35 | public function setServer($server) 36 | { 37 | $this->server = $server; 38 | } 39 | 40 | /** 41 | * @return int 0 or 1, or bool from parent 42 | */ 43 | public function isDownForMaintenance(): int 44 | { 45 | static $watch = null; 46 | if ($watch === null) { 47 | $watch = $this->getServer()->getConfig('watch_down'); 48 | } 49 | return $watch ? $this->server->getIntegerMemory('isDown') : parent::isDownForMaintenance(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/LaravelFly/Backup/Bootstrap/BackupAttributes.php: -------------------------------------------------------------------------------- 1 | backUpOnWorker(); 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /src/LaravelFly/Backup/Bootstrap/BackupConfigs.php: -------------------------------------------------------------------------------- 1 | setBackupedConfig(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/LaravelFly/Backup/Bootstrap/LoadConfiguration.php: -------------------------------------------------------------------------------- 1 | make('config'); 21 | 22 | $configCacheAlways = $appConfig['laravelfly.config_cache_always']; 23 | 24 | if ($configCacheAlways && file_exists($cacheFile = $app->bootstrapPath($this->service_cache_file))) { 25 | \LaravelFly\Fly::getServer()->echo( 26 | "include: $cacheFile 27 | if any configs or composer.json changed, please re-run 'php artisan config:clear'", 28 | 'NOTE',true 29 | ); 30 | list('psAcross' => $psAcross, 31 | 'psInRequest' => $psInRequest) = require $cacheFile; 32 | } else { 33 | 34 | if (!$appConfig['laravelfly']) { 35 | die("no file config/laravelfly.php, please run `php artisan vendor:publish --tag=fly-app`"); 36 | } 37 | 38 | $psInRequest = $appConfig['laravelfly.providers_in_request'] ?: []; 39 | 40 | $psAcross = array_diff( 41 | array_merge($appConfig['app.providers'], $app->make(PackageManifest::class)->providers()), 42 | $psInRequest, 43 | $appConfig['laravelfly.providers_ignore'] ?: [] 44 | ); 45 | 46 | if($configCacheAlways){ 47 | 48 | file_put_contents($cacheFile, ' $psAcross, 51 | 'psInRequest' => $psInRequest 52 | ], true) . 53 | ';' . PHP_EOL); 54 | 55 | \LaravelFly\Fly::getServer()->echo("cache created: $cacheFile",'INFO'); 56 | 57 | } 58 | 59 | } 60 | 61 | 62 | // 'app.providers' only providers across 63 | $appConfig['app.providers'] = $psAcross; 64 | 65 | $app->makeManifestForProvidersInRequest($psInRequest); 66 | 67 | 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/LaravelFly/Backup/Kernel.php: -------------------------------------------------------------------------------- 1 | app->bootProvidersInRequest();` 28 | // \Illuminate\Foundation\Bootstrap\BootProviders::class, 29 | 30 | \LaravelFly\Backup\Bootstrap\SetBackupForBaseServices::class, 31 | \LaravelFly\Backup\Bootstrap\BackupConfigs::class, 32 | \LaravelFly\Backup\Bootstrap\BackupAttributes::class, 33 | ]; 34 | /** 35 | * The application implementation. 36 | * 37 | * @var \LaravelFly\Backup\Application 38 | */ 39 | protected $app; 40 | 41 | /** 42 | * Override 43 | */ 44 | protected function sendRequestThroughRouter($request) 45 | { 46 | // if (!(LARAVELFLY_SERVICES['request'])) $this->app->instance('request', $request); 47 | 48 | // moved to Application::restoreAfterRequest 49 | // Facade::clearResolvedInstance('request'); 50 | 51 | // replace $this->bootstrap(); 52 | $this->app->boot(); 53 | 54 | return (new Pipeline($this->app)) 55 | ->send($request) 56 | ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) 57 | ->then($this->dispatchToRouter()); 58 | 59 | } 60 | /** 61 | * Override 62 | */ 63 | public function handle($request) 64 | { 65 | try { 66 | // moved to LaravelFlyServer::initAfterStart 67 | // $request::enableHttpMethodParameterOverride(); 68 | 69 | $response = $this->sendRequestThroughRouter($request); 70 | 71 | } catch (Exception $e) { 72 | $this->reportException($e); 73 | 74 | $response = $this->renderException($request, $e); 75 | } catch (Throwable $e) { 76 | 77 | $this->reportException($e = new FatalThrowableError($e)); 78 | 79 | $response = $this->renderException($request, $e); 80 | } 81 | 82 | $this->app['events']->dispatch( 83 | new Events\RequestHandled($request, $response) 84 | ); 85 | 86 | return $response; 87 | } 88 | 89 | 90 | } -------------------------------------------------------------------------------- /src/LaravelFly/Backup/ProviderRepository.php: -------------------------------------------------------------------------------- 1 | loadManifest(); 15 | 16 | if ($this->shouldRecompile($manifest, $providers)) { 17 | $manifest = $this->compileManifest($providers); 18 | } 19 | 20 | // this if is necessary 21 | if (isset($manifest['when'])) { 22 | foreach ($manifest['when'] as $provider => $events) { 23 | $this->registerLoadEvents($provider, $events); 24 | } 25 | } 26 | 27 | foreach ($manifest['eager'] as $provider) { 28 | $this->app->register($provider); 29 | } 30 | 31 | // this if is necessary 32 | if ($manifest['deferred']) { 33 | $this->app->addDeferredServices($manifest['deferred']); 34 | } 35 | } 36 | 37 | /** 38 | * treat deferred as eager 39 | * 40 | * @param array $providers 41 | */ 42 | public function loadForWorker(array $providers) 43 | { 44 | $manifest = $this->loadManifest(); 45 | 46 | if ($this->shouldRecompile($manifest, $providers)) { 47 | $manifest = $this->compileManifest($providers); 48 | } 49 | 50 | // this if is necessary 51 | if (isset($manifest['when'])) { 52 | foreach ($manifest['when'] as $provider => $events) { 53 | $this->registerLoadEvents($provider, $events); 54 | } 55 | } 56 | 57 | foreach ($manifest['eager'] as $provider) { 58 | $this->app->register($provider); 59 | } 60 | 61 | // this if is necessary 62 | if ($manifest['deferred']) { 63 | // hack, not deferred any more 64 | // $this->app->addDeferredServices($this->deferred = $manifest['deferred']); 65 | foreach ($manifest['deferred'] as $provider) { 66 | $this->app->register($provider); 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/LaravelFly/Backup/ProviderRepositoryInRequest.php: -------------------------------------------------------------------------------- 1 | loadManifest(); 25 | 26 | if ($this->shouldRecompile($manifest, $providers)) { 27 | $manifest = $this->compileManifest($providers); 28 | } 29 | 30 | $this->manifest = $manifest; 31 | } 32 | 33 | /** 34 | * Override 35 | * @param array [] it's useless, there should be an argument when overridde 36 | */ 37 | public function load(array $providers) 38 | { 39 | 40 | if(!($manifest = $this->manifest)) return; 41 | 42 | if (isset($manifest['when'])) { 43 | foreach ($manifest['when'] as $provider => $events) { 44 | $this->registerLoadEvents($provider, $events); 45 | } 46 | } 47 | 48 | if (isset($manifest['eager'])) { 49 | foreach ($manifest['eager'] as $provider) { 50 | $this->app->register($provider); 51 | } 52 | } 53 | 54 | if ($manifest['deferred']) { 55 | $this->app->addDeferredServices($manifest['deferred']); 56 | } 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/LaravelFly/Exception/LaravelFlyException.php: -------------------------------------------------------------------------------- 1 | config($options); 37 | 38 | static::$server->createSwooleServer(); 39 | 40 | return self::$server; 41 | } 42 | 43 | static protected function initEnv($options) 44 | { 45 | 46 | require_once __DIR__ . '/../functions.php'; 47 | 48 | if (class_exists('NunoMaduro\Collision\Provider')) 49 | (new \NunoMaduro\Collision\Provider)->register(); 50 | 51 | } 52 | 53 | public static function getServer($options = null):ServerInterface 54 | { 55 | 56 | if (!self::$server) { 57 | static::init($options); 58 | } 59 | return self::$server; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/LaravelFly/FpmLike/Application.php: -------------------------------------------------------------------------------- 1 | 5 | @include('laravel-fly::partials.table',['caption'=>'Server Info','data'=>$server]) 6 | 7 | @stop 8 | 9 | -------------------------------------------------------------------------------- /src/LaravelFly/FrontEnd/views/info/table-table.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravel-fly::layouts.info') 2 | 3 | @section('content') 4 | 5 | @foreach($info as $table) 6 | 9 | @if('grid'===($table['table_type']??'')) 10 | @include('laravel-fly::partials.grid',['caption'=>($loop->index+1).'. ' . $table['caption'],'data'=>$table['data'],'columns'=>$table['columns']]) 11 | @else 12 | @include('laravel-fly::partials.table',['caption'=>$table['caption'],'data'=>$table['data']]) 13 | @endif 14 | @endforeach 15 | @stop 16 | 17 | -------------------------------------------------------------------------------- /src/LaravelFly/FrontEnd/views/info/table.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravel-fly::layouts.info') 2 | 3 | @section('content') 4 | 5 | @include('laravel-fly::partials.table',['caption'=>$caption,'data'=>$info]) 6 |
7 | @stop 8 | 9 | -------------------------------------------------------------------------------- /src/LaravelFly/FrontEnd/views/layouts/info.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 | 16 | 17 |
18 |
19 |
20 |
21 | @yield('content') 22 |
23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /src/LaravelFly/FrontEnd/views/partials/grid.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @foreach($columns as $col) 5 | 6 | @endforeach 7 | 8 | @forelse($data as $item) 9 | 10 | @if(is_object($item)) 11 | @foreach($columns as $col) 12 | 15 | @endforeach 16 | @else 17 | {{--array, but not hash array --}} 18 | @foreach($item as $v) 19 | 22 | @endforeach 23 | @endif 24 | 25 | @empty 26 | 27 | 28 | 29 | @endforelse 30 |
{!! $caption !!} (worker id:{!! $WORKER_ID !!})
{!! $col !!}
13 | {!! \LaravelFly\FrontEnd\Controllers\InfoController::renderValue( $item->$col) !!} 14 | 20 | {!! \LaravelFly\FrontEnd\Controllers\InfoController::renderValue($v) !!} 21 |
(no data)
31 | 32 | -------------------------------------------------------------------------------- /src/LaravelFly/FrontEnd/views/partials/table.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | @forelse($data as $name=>$value) 4 | 5 | 6 | 7 | 8 | @empty 9 | 10 | 11 | 12 | @endforelse 13 |
{!! $caption !!} (worker id:{!! $WORKER_ID !!})
{!! $name !!}{!! \LaravelFly\FrontEnd\Controllers\InfoController::renderValue($value) !!}
(no data)
14 | 15 | -------------------------------------------------------------------------------- /src/LaravelFly/Kernel.php: -------------------------------------------------------------------------------- 1 | /app/Http/Kernel.php' at https://github.com/scil/LaravelFly/wiki/Configuration 5 | */ 6 | 7 | namespace LaravelFly; 8 | 9 | use Illuminate\Foundation\Http\Kernel as HttpKernel; 10 | 11 | if (defined('LARAVELFLY_MODE')) { 12 | 13 | class WhichKernel extends \LaravelFly\MidKernel{} 14 | 15 | } else { 16 | class WhichKernel extends HttpKernel{} 17 | } 18 | 19 | class Kernel extends WhichKernel 20 | { 21 | /** 22 | * The application's global HTTP middleware stack. 23 | * 24 | * These middleware are run during every request to your application. 25 | * 26 | * @var array 27 | */ 28 | protected $middleware = [ 29 | \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, 30 | \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, 31 | \App\Http\Middleware\TrimStrings::class, 32 | \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, 33 | \App\Http\Middleware\TrustProxies::class, 34 | ]; 35 | 36 | /** 37 | * The application's route middleware groups. 38 | * 39 | * @var array 40 | */ 41 | protected $middlewareGroups = [ 42 | 'web' => [ 43 | \App\Http\Middleware\EncryptCookies::class, 44 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 45 | \Illuminate\Session\Middleware\StartSession::class, 46 | // \Illuminate\Session\Middleware\AuthenticateSession::class, 47 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 48 | \App\Http\Middleware\VerifyCsrfToken::class, 49 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 50 | ], 51 | 52 | 'api' => [ 53 | 'throttle:60,1', 54 | 'bindings', 55 | ], 56 | ]; 57 | 58 | /** 59 | * The application's route middleware. 60 | * 61 | * These middleware may be assigned to groups or used individually. 62 | * 63 | * @var array 64 | */ 65 | protected $routeMiddleware = [ 66 | 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 67 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 68 | 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 69 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 70 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 71 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 72 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 73 | ]; 74 | } 75 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Bootstrap/CleanOnWorker.php: -------------------------------------------------------------------------------- 1 | resetServiceProviders(); 13 | 14 | $cloneServices = $app->cloneServices; 15 | 16 | // if( !LARAVELFLY_SERVICES['request'] ) $cloneServices = array_merge($cloneServices, ['request', 'url']); 17 | 18 | foreach ($cloneServices as $service) { 19 | 20 | // this is necessary for QUICK MAKE 21 | $app->forgetInstance($service); 22 | 23 | Facade::clearResolvedInstance($service); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Bootstrap/OnWork.php: -------------------------------------------------------------------------------- 1 | registerWorkerProviders(); 13 | $app->bootOnWorker(); 14 | $app->makeCFServices(); 15 | 16 | $config = $app->make('config'); 17 | 18 | if (LARAVELFLY_SERVICES['kernel']) { 19 | 20 | $app->setSingletonMiddlewares( 21 | $config->get('laravelfly.singleton_middlewares', []) 22 | ); 23 | } 24 | 25 | $router = $app->make('router'); 26 | 27 | $router->setSingletonMiddlewares( 28 | $config->get('laravelfly.singleton_route_middlewares', []) 29 | ); 30 | 31 | if (LARAVELFLY_SERVICES['routes'] && LARAVELFLY_SERVICES['kernel']) { 32 | $router->enableMiddlewareAlwaysStable(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Bootstrap/RegisterAcrossProviders.php: -------------------------------------------------------------------------------- 1 | registerAcrossProviders(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Bootstrap/ResolveSomeFacadeAliases.php: -------------------------------------------------------------------------------- 1 | bootstrapPath('/cache/laravelfly_aliases.php'); 34 | 35 | $configCacheAlways = $app->make('config')['laravelfly.config_cache_always']; 36 | 37 | if ($configCacheAlways && is_file($cacheFile)) { 38 | /** 39 | * not needed to check filemtime 40 | * @see:\LaravelFly\Map\Bootstrap\LoadConfiguration @unlink( $app->bootstrapPath('/cache/laravelfly_aliases.php')); 41 | * mtime of this file is always > '/cache/laravelfly_config.php' whose mtime > app.php or composer.lock 42 | */ 43 | return require $cacheFile; 44 | } 45 | 46 | 47 | $aliases = []; 48 | 49 | $all = array_keys(array_merge( 50 | $app->make('config')->get('app.aliases'), 51 | $app->make(PackageManifest::class)->aliases())); 52 | 53 | foreach ($all as $staticClass) { 54 | /** 55 | * @var $staticClass string 56 | */ 57 | if (in_array($staticClass, $this->black)) { 58 | continue; 59 | } 60 | 61 | try { 62 | $method = new \ReflectionMethod($staticClass, 'getFacadeAccessor'); 63 | } catch (\ReflectionException $e) { 64 | // Illuminate\Database\Eloquent\Model has no method getFacadeAccessor 65 | // todo: user model like User, Quote ? 66 | continue; 67 | } 68 | 69 | $method->setAccessible(true); 70 | $facadeAccessor = $method->invoke(null); 71 | 72 | if (is_object($facadeAccessor)) { 73 | // such as \Illuminate\Support\Facades\Blade 74 | continue; 75 | } 76 | 77 | if ($app->instanceResolvedOnWorker($facadeAccessor)) { 78 | $aliases[] = $staticClass; 79 | } 80 | } 81 | 82 | if($configCacheAlways){ 83 | 84 | file_put_contents($cacheFile, 'echo("cache created: $cacheFile"); 87 | 88 | } 89 | 90 | return $aliases; 91 | 92 | } 93 | 94 | public function bootstrap(Application $app) 95 | { 96 | foreach ($this->getAliases($app) as $staticClass) { 97 | /** 98 | * @var $staticClass \Illuminate\Support\Facades\Facade 99 | */ 100 | $staticClass::getFacadeRoot(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/CommonHack/Kernel.php: -------------------------------------------------------------------------------- 1 | app->instance('request', $request); 12 | 13 | /** 14 | * @var $this \LaravelFly\Map\Kernel 15 | */ 16 | return $this->router->dispatch($request); 17 | }; 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Auth/AuthManager.php: -------------------------------------------------------------------------------- 1 | null]; 16 | 17 | protected static $arrayAttriForObj = [ 18 | 'guards', 19 | // plus 20 | 'customCreators' 21 | ]; 22 | 23 | 24 | public function __construct($app) 25 | { 26 | parent::__construct($app); 27 | 28 | } 29 | 30 | protected function resolve($name) 31 | { 32 | $config = $this->getConfig($name); 33 | 34 | if (is_null($config)) { 35 | throw new InvalidArgumentException("Auth guard [{$name}] is not defined."); 36 | } 37 | 38 | if (isset(static::$corDict[\Swoole\Coroutine::getuid()]['customCreators'][$config['driver']])) { 39 | return $this->callCustomCreator($name, $config); 40 | } 41 | 42 | $driverMethod = 'create'.ucfirst($config['driver']).'Driver'; 43 | 44 | if (method_exists($this, $driverMethod)) { 45 | return $this->{$driverMethod}($name, $config); 46 | } 47 | 48 | throw new InvalidArgumentException( 49 | "Auth driver [{$config['driver']}] for guard [{$name}] is not defined." 50 | ); 51 | } 52 | 53 | protected function callCustomCreator($name, array $config) 54 | { 55 | return static::$corDict[\Swoole\Coroutine::getuid()]['customCreators'][$config['driver']]($this->app, $name, $config); 56 | } 57 | public function extend($driver, Closure $callback) 58 | { 59 | static::$corDict[\Swoole\Coroutine::getuid()]['customCreators'][$driver] = $callback; 60 | 61 | return $this; 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Auth/AuthManagerSame.php: -------------------------------------------------------------------------------- 1 | null]; 14 | 15 | protected static $arrayAttriForObj = [ 16 | 'guards', 17 | // 'customCreators' 18 | ]; 19 | 20 | 21 | public function __construct($app) 22 | { 23 | 24 | $this->app = $app; 25 | 26 | $this->initOnWorker(true); 27 | 28 | // this statement must be after initOnWorker 29 | static::$corDict[WORKER_COROUTINE_ID]['userResolver'] = function ($guard = null) { 30 | return $this->guard($guard)->user(); 31 | }; 32 | 33 | } 34 | 35 | public function guard($name = null) 36 | { 37 | $name = $name ?: $this->getDefaultDriver(); 38 | 39 | $cid = \Co::getUid(); 40 | return static::$corDict[$cid]['guards'][$name] ?? 41 | (static::$corDict[$cid]['guards'][$name] = $this->resolve($name)); 42 | } 43 | 44 | public function shouldUse($name) 45 | { 46 | $name = $name ?: $this->getDefaultDriver(); 47 | 48 | // todo this file seems useless? oh no! and we need work more! config maybe changed .see: 49 | // You may have wondered why when using the apigroup of middleware that $request->user() returns the correct user from the api guard and doesn't use the default web guard 50 | // https://asklagbox.com/blog/unboxing-laravel-authentication#the-user-resolver 51 | $this->setDefaultDriver($name); 52 | 53 | static::$corDict[\Co::getUid()]['userResolver'] = function ($name = null) { 54 | return $this->guard($name)->user(); 55 | }; 56 | } 57 | 58 | public function userResolver() 59 | { 60 | return static::$corDict[\Co::getUid()]['userResolver']; 61 | } 62 | 63 | public function resolveUsersUsing(Closure $userResolver) 64 | { 65 | static::$corDict[\Co::getUid()]['userResolver'] = $userResolver; 66 | 67 | return $this; 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Auth/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(GateContract::class, function ($app) { 20 | return new Gate($app, function () use ($app) { 21 | return call_user_func($app['auth']->userResolver()); 22 | }); 23 | }); 24 | } 25 | 26 | protected function registerAuthenticator() 27 | { 28 | $this->app->singleton('auth', function ($app) { 29 | // Once the authentication service has actually been requested by the developer 30 | // we will set a variable in the application indicating such. This helps us 31 | // know that we need to set any queued cookies in the after event later. 32 | $app['auth.loaded'] = true; 33 | 34 | // hack 35 | $class = LARAVELFLY_SERVICES['auth']? 36 | \LaravelFly\Map\Illuminate\Auth\AuthManagerSame::class: 37 | \LaravelFly\Map\Illuminate\Auth\AuthManager ::class; 38 | return new $class($app); 39 | }); 40 | 41 | $this->app->singleton('auth.driver', function ($app) { 42 | return $app['auth']->guard(); 43 | }); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Cookie/CookieJar.php: -------------------------------------------------------------------------------- 1 | '/', 17 | 'domain' => null, 18 | 'secure' => false, 19 | 'sameSite' => null, 20 | ]; 21 | 22 | public function __construct() 23 | { 24 | parent::__construct(); 25 | } 26 | 27 | protected function getPathAndDomain($path, $domain, $secure = null, $sameSite = null) 28 | { 29 | $dict = static::$corDict[\Swoole\Coroutine::getuid()]; 30 | 31 | return [$path ?: $dict['path'], $domain ?: $dict['domain'], is_bool($secure) ? $secure : $dict['secure'], $sameSite ?: $dict['sameSite']]; 32 | } 33 | 34 | public function setDefaultPathAndDomain($path, $domain, $secure = false, $sameSite = null) 35 | { 36 | $dict = &static::$corDict[\Swoole\Coroutine::getuid()]; 37 | 38 | [$dict['path'], $dict['domain'], $dict['secure'], $dict['sameSite'] ] = [$path, $domain, $secure, $sameSite]; 39 | 40 | return $this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Cookie/CookieJarSame.php: -------------------------------------------------------------------------------- 1 | initOnWorker( true); 19 | } 20 | 21 | public function queued($key, $default = null, $path = null) 22 | { 23 | $queued = Arr::get(static::$corDict[\Co::getUid()]['queued'], $key, $default); 24 | 25 | if ($path === null) { 26 | return Arr::last($queued, null, $default); 27 | } 28 | 29 | return Arr::get($queued, $path, $default); 30 | 31 | } 32 | 33 | public function queue(...$parameters) 34 | { 35 | if (head($parameters) instanceof Cookie) { 36 | $cookie = head($parameters); 37 | } else { 38 | $cookie = call_user_func_array([$this, 'make'], $parameters); 39 | } 40 | 41 | $Q = & static::$corDict[\Co::getUid()]['queued']; 42 | if (! isset($Q[$cookie->getName()])) { 43 | $Q[$cookie->getName()] = []; 44 | } 45 | 46 | $Q[$cookie->getName()][$cookie->getPath()] = $cookie; 47 | 48 | } 49 | 50 | public function unqueue($name, $path = null) 51 | { 52 | $Q = & static::$corDict[\Co::getUid()]['queued']; 53 | 54 | if ($path === null) { 55 | unset($Q[$name]); 56 | return; 57 | } 58 | 59 | unset($Q[$name][$path]); 60 | 61 | if (empty($Q[$name])) { 62 | unset($Q[$name]); 63 | } 64 | 65 | 66 | } 67 | 68 | public function getQueuedCookies() 69 | { 70 | return Arr::flatten(static::$corDict[\Co::getUid()]['queued']); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Cookie/CookieServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('cookie', function ($app) { 25 | $config = $app->make('config')->get('session'); 26 | 27 | $class = LARAVELFLY_SERVICES['cookie']? CookieJarSame::class: CookieJar::class; 28 | return (new $class)->setDefaultPathAndDomain( 29 | $config['path'], $config['domain'], $config['secure'], $config['same_site'] ?? null 30 | ); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connection/FakeSwooleConnTrait.php: -------------------------------------------------------------------------------- 1 | connected; 11 | * $connection->close(); 12 | * vendor/open-smf/connection-pool/src/Connectors/CoroutineMySQLConnector.php 13 | */ 14 | trait FakeSwooleConnTrait 15 | { 16 | 17 | /** 18 | * 19 | * @var \Swoole\Coroutine\Redis | \Swoole\Coroutine\MySQL 20 | */ 21 | protected $swooleConnection; 22 | 23 | 24 | /** 25 | * save config, only for reconnect 26 | * @param $config 27 | */ 28 | public function fake($swooleConnection) 29 | { 30 | $this->swooleConnection = $swooleConnection; 31 | } 32 | 33 | public function __get($key){ 34 | return $this->swooleConnection->{$key}; 35 | } 36 | 37 | public function __call($method, $parameters){ 38 | return $this->swooleConnection->$method(...$parameters); 39 | } 40 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connection/SwooleMySQLConnection.php: -------------------------------------------------------------------------------- 1 | swooleConnection = $this->getPdo()->getSwooleConnection(); 33 | } 34 | 35 | public function getDriverName() 36 | { 37 | return 'Swoole Coroutine MySQL'; 38 | } 39 | 40 | protected function tryAgainIfCausedByLostConnection(QueryException $e, $query, $bindings, \Closure $callback) 41 | { 42 | if ($this->causedByLostConnection($e->getPrevious()) || Str::contains($e->getMessage(), ['is closed', 'is not established'])) { 43 | $this->reconnect(); 44 | 45 | return $this->runQueryCallback($query, $bindings, $callback); 46 | } 47 | 48 | throw $e; 49 | } 50 | 51 | 52 | public function getClient(){ 53 | return $this->getPdo(); 54 | } 55 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/ConnectionFactory.php: -------------------------------------------------------------------------------- 1 | container->bound($key = "db.connector.{$config['driver']}")) { 23 | return $this->container->make($key); 24 | } 25 | 26 | switch ($config['driver']) { 27 | // hack 28 | case 'mysql': 29 | if ($config['coroutine'] ?? false) { 30 | return new MySQLLaravelConnector(); 31 | } 32 | } 33 | return parent::createConnector($config); 34 | } 35 | 36 | protected function createSingleConnection(array $config) 37 | { 38 | if (method_exists($this, 'createPdoResolver')) { 39 | $pdo = $this->createPdoResolver($config); 40 | } else { 41 | $pdo = $this->createConnector($config)->connect($config); 42 | } 43 | return $this->createSwooleConnection($config['driver'], $pdo, $config['database'], $config['prefix'], $config); 44 | } 45 | 46 | protected function createSwooleConnection($driver, $connection, $database, $prefix = '', array $config = []) 47 | { 48 | if (method_exists(Connection::class, 'getResolver')) { 49 | if ($resolver = Connection::getResolver($driver)) { 50 | return $resolver($connection, $database, $prefix, $config); 51 | } 52 | } else { 53 | if ($this->container->bound($key = "db.connection.{$driver}")) { 54 | return $this->container->make($key, [$connection, $database, $prefix, $config]); 55 | } 56 | } 57 | 58 | switch ($driver) { 59 | // hack 60 | case 'mysql': 61 | if ($config['coroutine'] ?? false) { 62 | return new SwooleMySQLConnection($connection, $database, $prefix, $config); 63 | } 64 | } 65 | return parent::createConnection($driver, $connection, $database, $prefix, $config); 66 | } 67 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connectors/MySQLConnectorTrait.php: -------------------------------------------------------------------------------- 1 | connect([ 19 | 'host' => Arr::get($config, 'host', '127.0.0.1'), 20 | 'port' => Arr::get($config, 'port', 3306), 21 | 'user' => Arr::get($config, 'username', 'hhxsv5'), 22 | 'password' => Arr::get($config, 'password', '52100'), 23 | 'database' => Arr::get($config, 'database', 'test'), 24 | 'timeout' => Arr::get($config, 'timeout', 5), 25 | 'charset' => Arr::get($config, 'charset', 'utf8mb4'), 26 | 'strict_type' => Arr::get($config, 'strict', false), 27 | ]); 28 | 29 | 30 | if (isset($config['timezone'])) { 31 | $connection->query('set time_zone="' . $config['timezone'] . '"'); 32 | } 33 | 34 | if (isset($config['strict'])) { 35 | if ($config['strict']) { 36 | $connection->query("set session sql_mode='STRICT_ALL_TABLES,ANSI_QUOTES'"); 37 | } else { 38 | $connection->query("set session sql_mode='ANSI_QUOTES'"); 39 | } 40 | } 41 | 42 | 43 | return $connection; 44 | } 45 | 46 | 47 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connectors/MySQLLaravelConnector.php: -------------------------------------------------------------------------------- 1 | connect($config); 28 | } catch (\Exception $e) { 29 | $mysql = $this->tryAgainIfCausedByLostConnectionForCoroutineMySQL($e, $config); 30 | } 31 | 32 | return $mysql; 33 | } 34 | 35 | public function connect(array $config) 36 | { 37 | return $this->connectSwoolePDO($config); 38 | 39 | } 40 | /** 41 | * @param \Throwable $e 42 | * @param array $config 43 | * @return SwoolePDO 44 | * @throws \Throwable 45 | */ 46 | protected function tryAgainIfCausedByLostConnectionForCoroutineMySQL($e, array $config) 47 | { 48 | if (parent::causedByLostConnection($e) || Str::contains($e->getMessage(), ['is closed', 'is not established'])) { 49 | return $this->connect($config); 50 | } 51 | throw $e; 52 | } 53 | 54 | 55 | 56 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connectors/MySQLSmfConnector.php: -------------------------------------------------------------------------------- 1 | connectSwoolePDO($config); 21 | 22 | } 23 | public function validate($connection): bool 24 | { 25 | return $connection instanceof SwooleMySQLConnection; 26 | } 27 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connectors/RedisConnectorTrait.php: -------------------------------------------------------------------------------- 1 | connect($config['host'], $config['port'], $config['var_serialize'] ?? false); 29 | 30 | $conn = new SwooleRedisConnection($swooleConn); 31 | return $conn; 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Connectors/RedisSmfConnector.php: -------------------------------------------------------------------------------- 1 | _connect($config,$config['options']??[]); 20 | 21 | } 22 | public function validate($connection): bool 23 | { 24 | return $connection instanceof SwooleRedisConnection; 25 | } 26 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/DatabaseServiceProvider.php: -------------------------------------------------------------------------------- 1 | initModel(); 25 | } 26 | 27 | public function initModel() 28 | { 29 | Model::initStaticForCorontine(WORKER_COROUTINE_ID); 30 | 31 | foreach (config('laravelfly.models_booted_on_work') as $class) { 32 | if (class_exists($class)){ 33 | new $class; 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Register the primary database bindings. 40 | * 41 | * @return void 42 | */ 43 | protected function registerConnectionServices() 44 | { 45 | $this->app->singleton('db.factory', function ($app) { 46 | // hack 47 | return new ConnectionFactory($app); 48 | }); 49 | 50 | $this->app->singleton('db', function ($app) { 51 | // hack 52 | return new DatabaseManager($app, $app['db.factory']); 53 | }); 54 | 55 | $this->app->bind('db.connection', function ($app) { 56 | return $app['db']->connection(); 57 | }); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/PDO/ConnectionException.php: -------------------------------------------------------------------------------- 1 | statement = $statement; 16 | } 17 | 18 | public function rowCount() 19 | { 20 | return $this->statement->affected_rows; 21 | } 22 | 23 | public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR) 24 | { 25 | $this->bindParams[$parameter] = $value; 26 | } 27 | 28 | /** 29 | * @param array $input_parameters 30 | * @return bool 31 | * @throws StatementException 32 | */ 33 | public function execute($input_parameters = null) 34 | { 35 | if (empty($input_parameters) && !empty($this->bindParams)) { 36 | $input_parameters = $this->bindParams; 37 | } 38 | $input_parameters = (array)$input_parameters; 39 | $this->result = $this->statement->execute($input_parameters, array_get($input_parameters, '__timeout__', -1)); 40 | if ($this->statement->errno != 0) { 41 | throw new StatementException($this->statement->error, $this->statement->errno); 42 | } 43 | return true; 44 | } 45 | 46 | public function fetchAll($how = null, $class_name = null, $ctor_args = null) 47 | { 48 | return $this->result; 49 | } 50 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Pool/Pool.php: -------------------------------------------------------------------------------- 1 | name = $name; 32 | 33 | /** 34 | * @var DatabaseManager| RedisManager $db 35 | */ 36 | $this->dbmgr = $db; 37 | 38 | } 39 | abstract function borrow(); 40 | abstract function return($conn); 41 | abstract function createConnection(); 42 | 43 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Pool/SimplePool.php: -------------------------------------------------------------------------------- 1 | size = $poolConfig['size'] ?? 20; 24 | } 25 | 26 | function initPool($name, $db,EventDispatcher $dispatcher ) 27 | { 28 | $this->_initPool($name, $db,$dispatcher); 29 | 30 | 31 | $this->pool = new \Swoole\Coroutine\Channel($this->size); 32 | for ($i = 0; $i < $this->size; $i++) { 33 | $this->return($this->createConnection()); 34 | } 35 | 36 | } 37 | 38 | function createConnection() 39 | { 40 | return $this->dbmgr->makeConnectionForPool($this->name); 41 | 42 | } 43 | 44 | public function borrow() 45 | { 46 | return $this->pool->pop(); 47 | } 48 | 49 | public function return($conn) 50 | { 51 | $this->pool->push($conn); 52 | } 53 | 54 | public function len() 55 | { 56 | return $this->pool->length(); 57 | } 58 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Database/Pool/SmfPool.php: -------------------------------------------------------------------------------- 1 | _initPool($name, $db,$dispatcher); 28 | 29 | $this->init(); 30 | 31 | $dispatcher->addListener('worker.stopped', function (GenericEvent $event) { 32 | $this->close(); 33 | }); 34 | 35 | 36 | } 37 | 38 | /** 39 | * disable go(). cor is not allowed during OnWorkerStart 40 | * @return bool 41 | */ 42 | public function init(): bool 43 | { 44 | if ($this->initialized) { 45 | return false; 46 | } 47 | $this->initialized = true; 48 | $this->pool = new Channel($this->maxActive); 49 | $this->balancerTimerId = $this->startBalanceTimer($this->idleCheckInterval); 50 | // hack 51 | // for LaravelFly, Coroutine can not be used before requests. 52 | // go(function () { 53 | for ($i = 0; $i < $this->minActive; $i++) { 54 | $connection = $this->createConnection(); 55 | $ret = $this->pool->push($connection, static::CHANNEL_TIMEOUT); 56 | if ($ret === false) { 57 | $this->removeConnection($connection); 58 | } 59 | } 60 | // }); 61 | return true; 62 | } 63 | protected function createConnection() 64 | { 65 | $this->connectionCount++; 66 | 67 | // - $connection = $this->connector->connect($this->connectionConfig); 68 | // + 69 | $connection = $this->dbmgr->makeConnectionForPool($this->name); 70 | 71 | 72 | $connection->{static::KEY_LAST_ACTIVE_TIME} = time(); 73 | return $connection; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connection/EnsureConnected.php: -------------------------------------------------------------------------------- 1 | client->close(); 12 | } 13 | 14 | public function ensureConnected() 15 | { 16 | // todo test 17 | // phpredis will attempt to reconnect so you can actually kill your own connection but may not notice losing it! 18 | // https://github.com/phpredis/phpredis 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connection/PredisConnection.php: -------------------------------------------------------------------------------- 1 | client->isConnected()){ 9 | $this->client->connect(); 10 | } 11 | ; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connection/SwooleRedisConnection.php: -------------------------------------------------------------------------------- 1 | swooleConnection = $client; 19 | } 20 | 21 | public function disconnect() 22 | { 23 | $this->swooleConnection->close(); 24 | } 25 | 26 | public function ensureConnected() 27 | { 28 | if (!$this->swooleConnection->connected){ 29 | $config = $this->config; 30 | 31 | $this->swooleConnection->connect($config['host'], $config['port'], $config['var_serialize'] ?? false); 32 | 33 | //todo 34 | // assert($this->client->connected === true); 35 | 36 | } 37 | } 38 | 39 | public function reconnect() 40 | { 41 | if ($this->swooleConnection->connected) 42 | $this->disconnect(); 43 | 44 | $config = $this->config; 45 | 46 | $this->swooleConnection->connect($config['host'], $config['port'], $config['var_serialize'] ?? false); 47 | 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connection/SwooleRedisNot.php: -------------------------------------------------------------------------------- 1 | createClient($config)); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connector/PredisConnector.php: -------------------------------------------------------------------------------- 1 | 10.0], $options, Arr::pull($config, 'options', []) 15 | ); 16 | 17 | // hack 18 | // disable persistent, we have got connection pool 19 | $config['persistent'] = false; 20 | 21 | return new PredisConnection(new Client($config, $formattedOptions)); 22 | } 23 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/Connector/SwooleRedisConnector.php: -------------------------------------------------------------------------------- 1 | _connect($config,$options); 20 | 21 | return $connection; 22 | } 23 | 24 | 25 | // todo 26 | public function connectToCluster(array $config, array $clusterOptions, array $options) 27 | { 28 | $options = array_merge($options, $clusterOptions, Arr::pull($config, 'options', [])); 29 | 30 | return new PhpRedisClusterConnection($this->createRedisClusterInstance( 31 | array_map([$this, 'buildClusterConnectionString'], $config), $options 32 | )); 33 | } 34 | 35 | protected function buildClusterConnectionString(array $server) 36 | { 37 | return $server['host'] . ':' . $server['port'] . '?' . http_build_query(Arr::only($server, [ 38 | 'database', 'password', 'timeout', 39 | ])); 40 | } 41 | 42 | 43 | // todo 44 | protected function createRedisClusterInstance(array $servers, array $options) 45 | { 46 | return new RedisCluster( 47 | null, 48 | array_values($servers), 49 | $options['timeout'] ?? 0, 50 | $options['read_timeout'] ?? 0, 51 | isset($options['persistent']) && $options['persistent'] 52 | ); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/RedisManager.php: -------------------------------------------------------------------------------- 1 | [name1 => conn1, name2 => conn2 ] 16 | * ] 17 | * 18 | * @var array $connections 19 | */ 20 | protected $connections = []; 21 | 22 | 23 | public function __construct($app,$driver, array $config) 24 | { 25 | parent::__construct($app,$driver, $config); 26 | 27 | $redis_config = app('config')['database.redis']; 28 | 29 | unset($redis_config['options'], $redis_config['client']); 30 | 31 | $this->initConnections($redis_config,'redis'); 32 | 33 | } 34 | 35 | /** 36 | * first ensureConnected then put back 37 | * 38 | * @param $cid 39 | */ 40 | function putBack($cid) 41 | { 42 | 43 | foreach ($this->connections[$cid] as $name => $conn) { 44 | /** 45 | * swooleredis or predis has no auto-reconnect 46 | * @var EnsureConnected $conn 47 | */ 48 | $conn->ensureConnected(); 49 | $this->pools[$name]->return($conn); 50 | } 51 | } 52 | 53 | public function connection($name = null) 54 | { 55 | $name = $name ?: 'default'; 56 | 57 | $cid = \Swoole\Coroutine::getuid(); 58 | 59 | if (!isset($this->connections[$cid][$name])) { 60 | 61 | return $this->connections[$cid][$name] = $this->pools[$name]->borrow(); 62 | } 63 | 64 | return $this->connections[$cid][$name]; 65 | 66 | } 67 | 68 | function makeConnectionForPool($name) 69 | { 70 | return $this->resolve($name); 71 | } 72 | 73 | 74 | public function connections() 75 | { 76 | return $this->connections[\Swoole\Coroutine::getuid()]; 77 | } 78 | 79 | 80 | protected function connector() 81 | { 82 | switch ($this->driver) { 83 | case 'predis': 84 | return new Connector\PredisConnector(); 85 | case 'phpredis': 86 | return new Connector\PhpRedisConnector(); 87 | case 'swoole-redis': 88 | return new Connector\SwooleRedisConnector(); 89 | 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Redis/RedisServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('redis', function ($app) { 18 | $config = $app->make('config')->get('database.redis'); 19 | 20 | // hack 21 | return new RedisManager($app, Arr::pull($config, 'client', 'predis'), $config); 22 | }); 23 | 24 | $this->app->bind('redis.connection', function ($app) { 25 | return $app['redis']->connection(); 26 | }); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/CookieSessionHandler.php: -------------------------------------------------------------------------------- 1 | null,]; 15 | 16 | public function __construct(CookieJar $cookie, $minutes) 17 | { 18 | $this->initOnWorker( true); 19 | parent::__construct($cookie, $minutes); 20 | } 21 | 22 | public function read($sessionId) 23 | { 24 | $value = static::$corDict[\Co::getUid()]['request']->cookies->get($sessionId) ?: ''; 25 | 26 | if (!is_null($decoded = json_decode($value, true)) && is_array($decoded)) { 27 | if (isset($decoded['expires']) && $this->currentTime() <= $decoded['expires']) { 28 | return $decoded['data']; 29 | } 30 | } 31 | 32 | return ''; 33 | } 34 | 35 | public function setRequest(Request $request) 36 | { 37 | static::$corDict[\Co::getUid()]['request'] = $request; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/DatabaseSessionHandler.php: -------------------------------------------------------------------------------- 1 | null,]; 21 | 22 | public function __construct(ConnectionInterface $connection, $table, $minutes, Container $container = null) 23 | { 24 | $this->initOnWorker( true); 25 | parent::__construct($connection, $table, $minutes, $container); 26 | } 27 | 28 | public function read($sessionId) 29 | { 30 | $session = (object)$this->getQuery()->find($sessionId); 31 | 32 | if ($this->expired($session)) { 33 | static::$corDict[\Co::getUid()]['exists'] = true; 34 | 35 | return ''; 36 | } 37 | 38 | if (isset($session->payload)) { 39 | static::$corDict[\Co::getUid()]['exists'] = true; 40 | 41 | return base64_decode($session->payload); 42 | } 43 | 44 | return ''; 45 | } 46 | 47 | public function write($sessionId, $data) 48 | { 49 | $payload = $this->getDefaultPayload($data); 50 | 51 | $cid = \Co::getUid(); 52 | 53 | if (!static::$corDict[$cid]['exists']) { 54 | $this->read($sessionId); 55 | } 56 | 57 | if (static::$corDict[$cid]['exists']) { 58 | $this->performUpdate($sessionId, $payload); 59 | } else { 60 | $this->performInsert($sessionId, $payload); 61 | } 62 | 63 | return static::$corDict[$cid]['exists'] = true; 64 | } 65 | 66 | public function setExists($value) 67 | { 68 | static::$corDict[\Co::getUid()]['exists'] = $value; 69 | 70 | return $this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/SessionManager.php: -------------------------------------------------------------------------------- 1 | buildSession(new CookieSessionHandler( 19 | $this->container->make('cookie'), $this->config->get('session.lifetime') 20 | )); 21 | } 22 | 23 | protected function createDatabaseDriver() 24 | { 25 | $table = $this->config->get('session.table'); 26 | 27 | $lifetime = $this->config->get('session.lifetime'); 28 | 29 | return $this->buildSession(new DatabaseSessionHandler( 30 | $this->getDatabaseConnection(), $table, $lifetime, $this->container 31 | )); 32 | } 33 | 34 | protected function createSwooleDriver() 35 | { 36 | /** 37 | * @var Common $server 38 | */ 39 | $server = \LaravelFly\Fly::getServer(); 40 | return $this->buildSession(new SwooleSessionHandler( 41 | $server->getTableMemory('swooleSession'), $this->config->get('session.lifetime') 42 | )); 43 | } 44 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/SessionServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerSessionManager(); 22 | 23 | $this->registerSessionDriver(); 24 | 25 | $this->app->singleton(\Illuminate\Session\Middleware\StartSession::class, function ($app) { 26 | // hack 27 | return new StartSession($app->make('session')); 28 | }); 29 | 30 | } 31 | 32 | protected function registerSessionManager() 33 | { 34 | 35 | $this->app->singleton('session', function ($app) { 36 | // hack 37 | $m = new SessionManager($app); 38 | 39 | // hack 40 | if (Fly::getServer()->getConfig('swoole_session_back')) { 41 | $m->setDefaultDriver('swoole'); 42 | Fly::getServer()->echo('config session.driver=swoole'); 43 | } 44 | 45 | return $m; 46 | 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/StartSession.php: -------------------------------------------------------------------------------- 1 | false,]; 20 | 21 | public function __construct(SessionManager $manager) 22 | { 23 | $this->manager = $manager; 24 | $this->initOnWorker( true); 25 | } 26 | */ 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Session/Swoole/SwooleSessionHandler.php: -------------------------------------------------------------------------------- 1 | table = $table; 28 | // (int)ini_get('session.gc_maxlifetime') ? 29 | $this->minutes = $minutes; 30 | } 31 | 32 | 33 | public function close() 34 | { 35 | return true; 36 | } 37 | 38 | 39 | public function destroy($session_id) 40 | { 41 | $this->table->del($session_id); 42 | } 43 | 44 | 45 | // todo cron job to clean old session? 46 | public function gc($maxlifetime) 47 | { 48 | // require pcre-devel 49 | foreach ($this->table as $key => $row) { 50 | /** 51 | * @var \swoole_table_row $row 52 | */ 53 | if ($row['last_activity'] > $maxlifetime) 54 | $this->table->del($key); 55 | 56 | } 57 | } 58 | 59 | function expired($lasttime) 60 | { 61 | return round(time() / 60) - $lasttime > $this->minutes; 62 | 63 | } 64 | 65 | public function open($save_path, $name) 66 | { 67 | return true; 68 | } 69 | 70 | 71 | public function read($session_id) 72 | { 73 | $data = $this->table->get($session_id); 74 | 75 | if (!$data) return ''; 76 | 77 | if ($this->expired($data['last_activity'])) { 78 | $this->table->del($session_id); 79 | return ''; 80 | } 81 | return $data['payload']; 82 | } 83 | 84 | 85 | public function write($session_id, $session_data) 86 | { 87 | $this->table->set($session_id, [ 88 | 'payload' => $session_data, 89 | 'last_activity' => round(time() / 60)]); 90 | } 91 | 92 | 93 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Translation/FileLoader.php: -------------------------------------------------------------------------------- 1 | jsonPaths, true)) 11 | $this->jsonPaths[] = $path; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Translation/TranslationServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerLoader(); 11 | 12 | $this->app->singleton('translator', function ($app) { 13 | $loader = $app['translation.loader']; 14 | 15 | $locale = $app['config']['app.locale']; 16 | 17 | // hack 18 | $trans = new Translator($loader, $locale); 19 | 20 | $trans->setFallback($app['config']['app.fallback_locale']); 21 | 22 | return $trans; 23 | }); 24 | } 25 | 26 | /** 27 | * Register the translation line loader. 28 | * 29 | * @return void 30 | */ 31 | protected function registerLoader() 32 | { 33 | $this->app->singleton('translation.loader', function ($app) { 34 | // hack 35 | return new FileLoader($app['files'], $app['path.lang']); 36 | }); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/Translation/Translator.php: -------------------------------------------------------------------------------- 1 | null, // see below 20 | ]; 21 | 22 | 23 | public function __construct(Loader $loader, $locale) 24 | { 25 | $this->loader = $loader; 26 | static::$normalAttriForObj = [ 27 | 'locale' => $locale, 28 | ]; 29 | 30 | $this->initOnWorker(true); 31 | } 32 | 33 | public function setLocale($locale) 34 | { 35 | static::$corDict[\Co::getUid()]['locale'] = $locale; 36 | } 37 | 38 | public function getLocale() 39 | { 40 | // \Log::info( 'translator cid is '. \Co::getUid() . implode("\n", array_keys(static::$corDict))); 41 | return static::$corDict[\Co::getUid()]['locale']; 42 | } 43 | 44 | public function get($key, array $replace = [], $locale = null, $fallback = true) 45 | { 46 | // hack 47 | $locale = $locale ?: static::$corDict[\Co::getUid()]['locale'] ; 48 | 49 | // For JSON translations, there is only one file per locale, so we will simply load 50 | // that file and then we will be ready to check the array for the key. These are 51 | // only one level deep so we do not need to do any fancy searching through it. 52 | $this->load('*', '*', $locale); 53 | 54 | $line = $this->loaded['*']['*'][$locale][$key] ?? null; 55 | 56 | // If we can't find a translation for the JSON key, we will attempt to translate it 57 | // using the typical translation file. This way developers can always just use a 58 | // helper such as __ instead of having to pick between trans or __ with views. 59 | if (! isset($line)) { 60 | [$namespace, $group, $item] = $this->parseKey($key); 61 | 62 | // Here we will get the locale that should be used for the language line. If one 63 | // was not passed, we will use the default locales which was given to us when 64 | // the translator was instantiated. Then, we can load the lines and return. 65 | $locales = $fallback ? $this->localeArray($locale) 66 | : [$locale]; 67 | 68 | foreach ($locales as $locale) { 69 | if (!is_null($line = $this->getLine( 70 | $namespace, $group, $locale, $item, $replace 71 | ))) { 72 | return $line ?? $key; 73 | } 74 | } 75 | } 76 | // If the line doesn't exist, we will return back the key which was requested as 77 | // that will be quick to spot in the UI if language keys are wrong or missing 78 | // from the application's language files. Otherwise we can return the line. 79 | return $this->makeReplacements($line ?: $key, $replace); 80 | } 81 | 82 | 83 | 84 | protected function localeArray($locale) 85 | { 86 | return array_filter([$locale ?: static::$corDict[\Co::getUid()]['locale'], $this->fallback]); 87 | } 88 | 89 | 90 | protected function localeForChoice($locale) 91 | { 92 | return $locale ?: static::$corDict[\Co::getUid()]['locale'] ?: $this->fallback; 93 | } 94 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/View/BladeCompiler_1.php: -------------------------------------------------------------------------------- 1 | compiledPath, 20 | * path2=>... 21 | * ] 22 | * @var array 23 | */ 24 | protected static $mapFly = []; 25 | 26 | public function getCompiledPath($path) 27 | { 28 | if (isset(static::$mapFly[$path])) { 29 | return static::$mapFly[$path]; 30 | } 31 | 32 | $compiled = parent::getCompiledPath($path); 33 | 34 | $this->setInfo($path, $compiled); 35 | 36 | return $compiled; 37 | } 38 | 39 | protected function setInfo($path, $compiled) 40 | { 41 | static::$mapFly[$path] = $compiled; 42 | 43 | // compile if compiled not exists or old 44 | if (!$this->files->exists($compiled) || 45 | $this->files->lastModified($compiled) <= $this->files->lastModified($path)) { 46 | $this->compile($path); 47 | } 48 | 49 | } 50 | 51 | public function isExpired($path) 52 | { 53 | if (!isset(static::$mapFly[$path])) { 54 | $this->setInfo($path, parent::getCompiledPath($path)); 55 | } else if (!$this->files->exists(static::$mapFly[$path])) { 56 | // avoid the risk: the compiled file deleted when the swoole worker is working 57 | $this->compile($path); 58 | } 59 | return false; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/View/Factory.php: -------------------------------------------------------------------------------- 1 | 0]; 24 | 25 | protected static $arrayAttriForObj = ['shared', 26 | // ManagesLayouts 27 | 'sections', 'sectionStack', 28 | // ManagesComponents 29 | 'componentStack', 'componentData', 'slots', 'slotStack', 30 | // ManagesLoops 31 | 'loopsStack', 32 | // ManagesStacks 33 | 'pushes', 'prepends', 'pushStack', 34 | // ManagesTranslations 35 | 'translationReplacements', 36 | ]; 37 | 38 | protected static $arrayStaticAttri = [ 39 | // ManagesLayouts 40 | 'parentPlaceholder']; 41 | 42 | public function __construct(EngineResolver $engines, ViewFinderInterface $finder, Dispatcher $events) 43 | { 44 | $this->initOnWorker( true); 45 | static::initStaticForCorontine(-1, true); 46 | 47 | // this line must be the last, because it visit share 48 | parent::__construct($engines, $finder, $events); 49 | } 50 | 51 | public function share($key, $value = null) 52 | { 53 | $keys = is_array($key) ? $key : [$key => $value]; 54 | 55 | $cid = \Co::getUid(); 56 | foreach ($keys as $key => $value) { 57 | static::$corDict[$cid]['shared'][$key] = $value; 58 | } 59 | 60 | return $value; 61 | } 62 | 63 | public function shared($key, $default = null) 64 | { 65 | return Arr::get(static::$corDict[\Co::getUid()]['shared'], $key, $default); 66 | } 67 | 68 | public function getShared() 69 | { 70 | return static::$corDict[\Co::getUid()]['shared']; 71 | } 72 | 73 | public function incrementRender() 74 | { 75 | static::$corDict[\Co::getUid()]['renderCount']++; 76 | } 77 | 78 | public function decrementRender() 79 | { 80 | static::$corDict[\Co::getUid()]['renderCount']--; 81 | } 82 | 83 | /** 84 | * Check if there are no active render operations. 85 | * 86 | * @return bool 87 | */ 88 | public function doneRendering() 89 | { 90 | return static::$corDict[\Co::getUid()]['renderCount'] == 0; 91 | } 92 | 93 | public function flushState() 94 | { 95 | static::$corDict[\Co::getUid()]['renderCount'] = 0; 96 | 97 | $this->flushSections(); 98 | $this->flushStacks(); 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/Illuminate/View/ViewServiceProvider.php: -------------------------------------------------------------------------------- 1 | register is called on work, and never called in any requests. 22 | * I think it's rare to call it in a request. 23 | * 24 | * @See: Illuminate\View\Engines::register() 25 | */ 26 | 27 | return ['view', 'view.engine.resolver', 'blade.compiler']; 28 | } 29 | 30 | protected function createFactory($resolver, $finder, $events) 31 | { 32 | // hack 33 | return new Factory($resolver, $finder, $events); 34 | } 35 | 36 | 37 | /** 38 | * overwwite laravel offical's 'blade.compiler' to cache view's info 39 | * 40 | * @param EngineResolver $resolver 41 | */ 42 | public function registerBladeEngine($resolver) 43 | { 44 | $this->app->singleton('blade.compiler', function () { 45 | if(config('laravelfly.view_compile_1')) 46 | // hack: Cache for view compiled path. 47 | return new BladeCompiler_1( 48 | $this->app['files'], $this->app['config']['view.compiled'] 49 | ); 50 | 51 | return new BladeCompiler( 52 | $this->app['files'], $this->app['config']['view.compiled'] 53 | ); 54 | }); 55 | 56 | $resolver->register('blade', function () { 57 | return new CompilerEngine($this->app['blade.compiler']); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/LaravelFly/Map/IlluminateBase/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('events', function ($app) { 17 | return (new Dispatcher($app))->setQueueResolver(function () use ($app) { 18 | return $app->make(QueueFactoryContract::class); 19 | }); 20 | }); 21 | } 22 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/IlluminateBase/SingletonRequestException.php: -------------------------------------------------------------------------------- 1 | $defaultValue) { 30 | if (is_callable($defaultValue)) { 31 | static::$corDict[WORKER_COROUTINE_ID][$attri] = $defaultValue(); 32 | } else { 33 | static::$corDict[WORKER_COROUTINE_ID][$attri] = $defaultValue; 34 | } 35 | } 36 | } 37 | 38 | if ($listen) { 39 | $event = Container::getInstance()->make('events'); 40 | 41 | $event->listen('request.corinit', function ($cid) { 42 | $this->initForRequestCorontine($cid); 43 | }); 44 | 45 | $event->listen('request.corunset', function ($cid) { 46 | $this->unsetForRequestCorontine($cid); 47 | }); 48 | 49 | 50 | $event->listen('usercor.init', function ($parentId, $childId) { 51 | $this->initUserCoroutine($parentId, $childId); 52 | }); 53 | 54 | $event->listen('usercor.unset', function ($childId) { 55 | $this->unsetUserCoroutine($childId); 56 | 57 | }); 58 | $event->listen('usercor.unset2', function ($parentId, $childId) { 59 | $this->unsetUserCoroutine2($parentId, $childId); 60 | 61 | }); 62 | } 63 | 64 | 65 | } 66 | 67 | function initForRequestCorontine($cid) 68 | { 69 | static::$corDict[$cid] = static::$corDict[WORKER_COROUTINE_ID]; 70 | 71 | // $f = fopen('/vagrant/www/zc/tmp', 'a+'); 72 | // fwrite($f, "\ninited ". get_class($this) ."\n ". implode(',', array_keys(static::$corDict)) ); 73 | // fclose($f); 74 | } 75 | 76 | function unsetForRequestCorontine(int $cid) 77 | { 78 | unset(static::$corDict[$cid]); 79 | } 80 | 81 | function initUserCoroutine($parentId, $childId) 82 | { 83 | static::$corDict[$childId] = static::$corDict[$parentId]; 84 | } 85 | 86 | function unsetUserCoroutine($childId) 87 | { 88 | 89 | unset(static::$corDict[$childId]); 90 | } 91 | 92 | function unsetUserCoroutine2($parentId, $childId) 93 | { 94 | /** 95 | * 96 | * low risk? 97 | * Does \Co::getUid() has the possibility to return same number in a worker process? #1977 98 | * https://github.com/swoole/swoole-src/issues/1977#issuecomment-422232642 99 | */ 100 | if (isset(static::$corDict[$parentId])) 101 | static::$corDict[$parentId] = static::$corDict[$childId]; 102 | 103 | unset(static::$corDict[$childId]); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/LaravelFly/Map/Util/StaticDict.php: -------------------------------------------------------------------------------- 1 | $defaultValue) { 25 | if (is_callable($defaultValue)) { 26 | static::$corStaticDict[WORKER_COROUTINE_ID][$attri] = $defaultValue(); 27 | } else { 28 | static::$corStaticDict[WORKER_COROUTINE_ID][$attri] = $defaultValue; 29 | } 30 | } 31 | } 32 | 33 | if ($listen) { 34 | $event = Container::getInstance()->make('events'); 35 | 36 | $event->listen('request.corinit', function ($cid) { 37 | static::initStaticForRequestCorontine($cid); 38 | }); 39 | 40 | $event->listen('request.corunset', function ($cid) { 41 | static::unsetStaticForRequestCorontine($cid); 42 | }); 43 | 44 | $event->listen('usercor.init', function ($parentId, $childId) { 45 | static::initStaticUserCoroutine($parentId, $childId); 46 | }); 47 | 48 | $event->listen('usercor.unset', function ($childId) { 49 | static::unsetStaticUserCoroutine($childId); 50 | }); 51 | $event->listen('usercor.unset2', function ($parentId, $childId, $write) { 52 | static::unsetStaticUserCoroutine2($parentId, $childId, $write); 53 | }); 54 | 55 | } 56 | 57 | 58 | } 59 | 60 | static function initStaticForRequestCorontine($cid) 61 | { 62 | static::$corStaticDict[$cid] = static::$corStaticDict[WORKER_COROUTINE_ID]; 63 | } 64 | 65 | static function unsetStaticForRequestCorontine(int $cid) 66 | { 67 | unset(static::$corStaticDict[$cid]); 68 | } 69 | 70 | static function initStaticUserCoroutine($parentId, $childId) 71 | { 72 | static::$corStaticDict[$childId] = static::$corStaticDict[$parentId]; 73 | } 74 | 75 | static function unsetStaticUserCoroutine($childId) 76 | { 77 | unset(static::$corStaticDict[$childId]); 78 | } 79 | 80 | static function unsetStaticUserCoroutine2($parentId, $childId, $write) 81 | { 82 | if ($write && isset(static::$corStaticDict[$parentId])) 83 | static::$corStaticDict[$parentId] = static::$corStaticDict[$childId]; 84 | 85 | unset(static::$corStaticDict[$childId]); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/LaravelFly/Providers/ConfigCacheCommand.php: -------------------------------------------------------------------------------- 1 | call('config:clear'); 20 | 21 | $config = $this->getFreshConfiguration(); 22 | 23 | $serverConfigFile = $this->argument('serverConfigFile') ?: $this->laravel->basePath() . '/fly.conf.php'; 24 | 25 | if (!is_file($serverConfigFile)) 26 | $this->error("LaravelFly server conf file not exists: $serverConfigFile"); 27 | 28 | // var $basePath is useful, can be used in $serverConfigFile 29 | $basePath = $this->getApplication()->getLaravel()->basePath(); 30 | include $serverConfigFile; 31 | $this->info("[LaravelFly] server conf file $serverConfigFile included."); 32 | 33 | $allConfig = $this->getFreshConfiguration(); 34 | 35 | $this->files->put( 36 | $this->laravel->getCachedConfigPath(), 'info('Configuration cached successfully!'); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/LaravelFly/Providers/ConfigClearCommand.php: -------------------------------------------------------------------------------- 1 | laravel->bootstrapPath('cache/laravelfly_ps_simple.php')); 21 | @unlink($this->laravel->bootstrapPath('cache/laravelfly_ps_map.php')); 22 | @unlink($this->laravel->bootstrapPath('cache/laravelfly_aliases.php')); 23 | $this->info("LaravelFly ps configuration cache cleard!"); 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelFly/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | app['config']->get('laravelfly.web',[]); 24 | 25 | if(empty($config['enable'])) return; 26 | 27 | $prefix = $config['prefix'] ?? 'laravel-fly'; 28 | \View::share('LARAVEL_FLY_PREFIX',$prefix); 29 | 30 | $this->server = $this->app->getServer(); 31 | $swoole = $this->swoole = $this->server->getSwooleServer(); 32 | \View::share('WORKER_PID' , $swoole->worker_pid); 33 | \View::share('WORKER_ID' , $swoole->worker_id); 34 | 35 | \View::share('INFO_ITEMS',['info','header','eventListeners','routes','routesWorker','db']); 36 | 37 | $routeConfig = [ 38 | 'namespace' => 'LaravelFly\FrontEnd\Controllers', 39 | 'prefix' => $prefix, 40 | // 'domain' => !empty($config['domain']) ? $config['domain'] : '', 41 | // 'middleware' => [DebugbarEnabled::class], 42 | 43 | ]; 44 | 45 | $this->loadViewsFrom(__DIR__.'/../FrontEnd/views', 'laravel-fly'); 46 | 47 | $this->getRouter()->group($routeConfig, function ($router) { 48 | $router->get('info/{sub?}', [ 49 | 'uses' => 'InfoController@index', 50 | 'as' => 'laravelfly.info', 51 | ]); 52 | }); 53 | 54 | } 55 | 56 | /** 57 | * Get the active router. 58 | * 59 | * @return Router 60 | */ 61 | protected function getRouter() 62 | { 63 | return $this->app['router']; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/LaravelFly/Providers/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->extend('command.config.cache', function ($old,$app) { 13 | return new ConfigCacheCommand($app['files']); 14 | }); 15 | 16 | // overwrite artisan-command config.clear 17 | $this->app->extend('command.config.clear', function ($old,$app) { 18 | return new ConfigClearCommand($app['files']); 19 | }); 20 | } 21 | 22 | public function boot() 23 | { 24 | // php artisan vendor:publish --tag=fly-app 25 | $this->publishes([ 26 | __DIR__ . '/../../../config/laravelfly-app-config.example.php' => config_path('laravelfly.php'), 27 | ], 'fly-app'); 28 | 29 | // php artisan vendor:publish --tag=fly-server 30 | $this->publishes([ 31 | __DIR__ . '/../../../config/laravelfly-server-config.example.php' => base_path('fly.conf.php'), 32 | ], 'fly-server'); 33 | } 34 | } -------------------------------------------------------------------------------- /src/LaravelFly/Server/FpmHttpServer.php: -------------------------------------------------------------------------------- 1 | swoole->on('request', array($this, 'onRequest')); 15 | } 16 | 17 | 18 | public function onRequest(\swoole_http_request $request, \swoole_http_response $response) 19 | { 20 | 21 | /** @var \LaravelFly\FpmLike\Application $app */ 22 | $app = new $this->appClass($this->root); 23 | 24 | $app->setServer($this); 25 | 26 | $app->singleton( 27 | \Illuminate\Contracts\Http\Kernel::class, 28 | \App\Http\Kernel::class 29 | ); 30 | $app->singleton( 31 | \Illuminate\Contracts\Debug\ExceptionHandler::class, 32 | \App\Exceptions\Handler::class 33 | ); 34 | 35 | $event = new GenericEvent(null, ['server' => $this, 'app' => $app, 'request' => $request]); 36 | $this->dispatcher->dispatch('laravel.ready', $event); 37 | $this->echo("event laravel.ready with $this->appClass instanced in pid " . getmypid()); 38 | 39 | $this->setGlobal($request); 40 | 41 | $laravel_request = \Illuminate\Http\Request::createFromBase(\Symfony\Component\HttpFoundation\Request::createFromGlobals()); 42 | 43 | $kernel = $app->make(\Illuminate\Contracts\Http\Kernel::class); 44 | 45 | $laravel_response = $kernel->handle( 46 | // $request = \Illuminate\Http\Request::capture() 47 | $laravel_request 48 | ); 49 | 50 | $this->swooleResponse($response, $laravel_response); 51 | 52 | $kernel->terminate($laravel_request, $laravel_response); 53 | 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/LaravelFly/Server/ServerInterface.php: -------------------------------------------------------------------------------- 1 | input = new \Symfony\Component\Console\Input\ArgvInput(), 38 | $input = $this->input = new \Psy\Input\ShellInput(''); 39 | 40 | $output = $this->output = new \Illuminate\Console\OutputStyle($input, new ConsoleOutput()); 41 | 42 | // vendor/composer/composer/src/Composer/Console/Application.php 43 | $io = $this->io = new ConsoleIO($input, $output, (new \Symfony\Component\Console\Application())->getHelperSet()); 44 | 45 | // if ($this->input->isInteractive()) { 46 | // $description = $io->ask('Are you sure to go on? ', 'n'); 47 | // $this->output->writeln($description); 48 | // } 49 | } 50 | function echo($text, $status = 'INFO', $color = false) 51 | { 52 | 53 | switch ($status) { 54 | case 'INFO': 55 | $level = 3; 56 | break; 57 | case 'NOTE': 58 | $level = 2; 59 | break; 60 | case 'WARN': 61 | $level = 1; 62 | break; 63 | case 'ERR': 64 | $level = 0; 65 | break; 66 | default: 67 | $level = 0; 68 | } 69 | if ($level <= $this->echoLevel) { 70 | $text = "[$status] $text\n"; 71 | echo $color ? $this->colorize($text, $status) : $text; 72 | } 73 | 74 | } 75 | 76 | function echoOnce($text, $status = 'INFO', $color = false) 77 | { 78 | if ($this->currentWorkerID === 0) { 79 | $this->echo($text, $status, $color); 80 | } 81 | } 82 | 83 | function colorize($text, $status) 84 | { 85 | if (!$this->colorize) return $text; 86 | 87 | $out = ""; 88 | switch ($status) { 89 | case "WARN": 90 | $out = "[41m"; //Red background 91 | break; 92 | case "NOTE": 93 | $out = "[43m"; //Yellow background 94 | // $out = "[44m"; //Blue background 95 | break; 96 | case "SUCCESS": 97 | $out = "[42m"; //Green background 98 | break; 99 | case "ERR": 100 | $out = "[41m"; //Red background 101 | break; 102 | default: 103 | throw new \Exception("Invalid status: " . $status); 104 | } 105 | return chr(27) . "$out" . "$text" . chr(27) . "[0m"; 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/DispatchRequestByQuery.php: -------------------------------------------------------------------------------- 1 | echo('worker_num is 1, dispatch_by_query is useless', 'INFO'); 19 | return; 20 | } 21 | 22 | if (isset($options['dispatch_func'])) { 23 | $this->echo('dispatch_func is set, dispatch_by_query is disabled', 'INFO'); 24 | return; 25 | } 26 | 27 | $options['dispatch_func'] = [$this, 'dispatch']; 28 | 29 | $this->workerIdsTable($options); 30 | } 31 | 32 | // must be public 33 | public function dispatch($swoole_server, $fd, $type, $data) 34 | { 35 | if (preg_match('/worker-(id|pid)(?:=|:\s+)(\d+)/i', $data, $matches)) { 36 | 37 | // 'id' or 'Id', neither 'pid' or 'Pid' 38 | if (strlen($matches[1]) === 2) { 39 | $id = (int)($matches[2]) % $swoole_server->setting['worker_num']; 40 | $this->echo("dispatch worker $id by worker-id={$matches[2]}", 'INFO'); 41 | return $id; 42 | } 43 | 44 | foreach ($swoole_server->fly->getWorkerIds() as $row) { 45 | if ($row['pid'] == $matches[2]) { 46 | $id = $row['id']; 47 | $this->echo("dispatch worker $id by worker-pid={$matches[2]}"); 48 | return $id; 49 | } 50 | } 51 | 52 | } 53 | 54 | $this->echo("dispatch worker by $fd % {$swoole_server->setting['worker_num']}"); 55 | return $fd % $swoole_server->setting['worker_num']; 56 | } 57 | 58 | 59 | 60 | protected function workerIdsTable($options) 61 | { 62 | $this->tableMemory['workerIds'] = $table = new \swoole_table($options['worker_num']); 63 | 64 | $table->column('id', \swoole_table::TYPE_INT, 1); 65 | $table->column('pid', \swoole_table::TYPE_INT, 3); 66 | $table->create(); 67 | $this->workerIdsSubscriber(); 68 | } 69 | 70 | function workerIdsSubscriber() 71 | { 72 | $this->dispatcher->addListener('worker.starting', function (GenericEvent $event) { 73 | $this->tableMemory['workerIds']->set($event['workerid'], ['id' => $event['workerid'], 'pid' => getmypid()]); 74 | }); 75 | $this->dispatcher->addListener('worker.stopped', function (GenericEvent $event) { 76 | $this->tableMemory['workerIds']->del($event['workerid']); 77 | }); 78 | } 79 | 80 | function getWorkerIds() 81 | { 82 | return $this->tableMemory['workerIds']; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/Preloader.php: -------------------------------------------------------------------------------- 1 | echo("include pre-files"); 24 | 25 | foreach ($this->getClassFiles() as $f) { 26 | try { 27 | $file = realpath($f); 28 | if (!is_string($file) || empty($file)) { 29 | $this->echo(".. Invalid pre-include filename $f provided.",'WARN'); 30 | continue; 31 | } 32 | 33 | if (!is_readable($file)) { 34 | $this->echo(".. Cannot open pre-include $f for reading.",'WARN'); 35 | continue; 36 | } 37 | 38 | include $file; 39 | } catch (\Exception $e) { 40 | // 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * Get the classes that should be combined and compiled. 47 | * 48 | * @return array 49 | */ 50 | protected function getClassFiles() 51 | { 52 | $core = require __DIR__ . '/preloader_config_onlymapmode.php'; 53 | 54 | // Map mode has loaded fly files and related files, so non-Map mode can load more files now 55 | if ($this->getConfig('mode') !== 'Map') { 56 | $core = $core + (require __DIR__ . '/preloader_config_more.php'); 57 | if (!$this->getConfig('log_cache')) { 58 | $core[] = $this->root . '/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php'; 59 | $core[] = $this->root . '/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php'; 60 | $core[] = $this->root . '/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php'; 61 | $core[] = $this->root . '/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php'; 62 | } 63 | } 64 | 65 | return $core + ( $this->getConfig('pre_files') ?: []); 66 | } 67 | 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/ShareInteger.php: -------------------------------------------------------------------------------- 1 | integerMemory[$name] = $atom; 22 | } 23 | 24 | /** 25 | * @param string $name 26 | * @return int|null 27 | */ 28 | public function getIntegerMemory(string $name): ?int 29 | { 30 | if (array_key_exists($name, $this->integerMemory)) { 31 | return $this->integerMemory[$name]->get(); 32 | } 33 | return null; 34 | } 35 | 36 | function setIntegerMemory(string $name, $value, $when = null) 37 | { 38 | if (array_key_exists($name, $this->integerMemory)) { 39 | if ($when) { 40 | return $this->integerMemory[$name]->cmpset($when, (int)$value); 41 | } 42 | $this->integerMemory[$name]->set((int)$value); 43 | } 44 | } 45 | 46 | function addIntegerMemory(string $name, $value) 47 | { 48 | if (array_key_exists($name, $this->integerMemory)) 49 | $this->integerMemory[$name]->add((int)$value); 50 | } 51 | 52 | function subIntegerMemory(string $name, $value) 53 | { 54 | if (array_key_exists($name, $this->integerMemory)) 55 | $this->integerMemory[$name]->sub((int)$value); 56 | } 57 | } -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/ShareTable.php: -------------------------------------------------------------------------------- 1 | tableMemory[$name]; 24 | } 25 | 26 | public function newTableMemory(string $name, int $size, array $columns, $class = \swoole_table::class): \swoole_table 27 | { 28 | 29 | $table = new $class($size); 30 | 31 | foreach ($columns as $colName => $config) { 32 | $table->column($colName, $config[0], $config[1]); 33 | } 34 | 35 | $table->create(); 36 | 37 | $this->tableMemory[$name] = $table; 38 | 39 | $this->{$name . 'Table'} = $table; 40 | 41 | $memory = $table->getMemorySize(); 42 | $memory = round($memory / 1024 / 1024, 2); 43 | 44 | $this->echo("table $name created. row: $size; memory: {$memory}M"); 45 | 46 | return $table; 47 | } 48 | 49 | function dumpSessionTable() 50 | { 51 | 52 | } 53 | 54 | function restoreSessionTable() 55 | { 56 | 57 | } 58 | 59 | 60 | 61 | } -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/Tinker.php: -------------------------------------------------------------------------------- 1 | echo( 18 | "daemonize disabled to allow tinker run normally", 19 | 'WARN',true 20 | ); 21 | } 22 | 23 | if ($options['worker_num'] == 1) { 24 | $this->echo( 25 | "worker_num is 1, the server can not response any other requests when using tinker", 26 | 'WARN',true 27 | ); 28 | } 29 | 30 | $this->tinkerSubscriber(); 31 | 32 | } 33 | 34 | protected function tinkerSubscriber() 35 | { 36 | 37 | $this->dispatcher->addListener('worker.starting', function (GenericEvent $event) { 38 | \LaravelFly\Tinker\Shell::make($event['server']); 39 | 40 | \LaravelFly\Tinker\Shell::addAlias([ 41 | \LaravelFly\Fly::class, 42 | ]); 43 | }); 44 | 45 | $this->dispatcher->addListener('laravel.ready', function (GenericEvent $event) { 46 | $event['app']->instance('tinker', \LaravelFly\Tinker\Shell::$instance); 47 | }); 48 | 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/LaravelFly/Server/Traits/preloader_config_more.php: -------------------------------------------------------------------------------- 1 | root; 13 | return [ 14 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Container/Container.php', 15 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php', 16 | 17 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Support/Arrayable.php', 18 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Support/Jsonable.php', 19 | 20 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Routing/Registrar.php', 21 | $basePath.'/vendor/laravel/framework/src/Illuminate/Contracts/Http/Kernel.php', 22 | $basePath.'/vendor/laravel/framework/src/Illuminate/Container/Container.php', 23 | $basePath.'/vendor/symfony/http-kernel/HttpKernelInterface.php', 24 | 25 | // for server config 'log_cache' 26 | // $basePath . '/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php', 27 | // $basePath . '/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php', 28 | // $basePath . '/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php', 29 | // $basePath . '/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php', 30 | 31 | $basePath . '/vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesComponents.php', 32 | $basePath . '/vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesLayouts.php', 33 | $basePath . '/vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesLoops.php', 34 | $basePath . '/vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesStacks.php', 35 | $basePath . '/vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesTranslations.php', 36 | 37 | $basePath.'/vendor/laravel/framework/src/Illuminate/Foundation/Application.php', 38 | $basePath.'/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php', 39 | $basePath.'/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php', 40 | $basePath.'/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php', 41 | $basePath.'/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php', 42 | $basePath.'/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php', 43 | $basePath.'/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php', 44 | $basePath.'/vendor/laravel/framework/src/Illuminate/Routing/Router.php', 45 | $basePath.'/vendor/laravel/framework/src/Illuminate/Routing/Controller.php', 46 | $basePath.'/vendor/laravel/framework/src/Illuminate/Support/Collection.php', 47 | $basePath.'/vendor/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 48 | $basePath.'/vendor/laravel/framework/src/Illuminate/View/FileViewFinder.php', 49 | ]; 50 | -------------------------------------------------------------------------------- /src/LaravelFly/Server/WebSocketServer.php: -------------------------------------------------------------------------------- 1 | app or $this->kernel 17 | * 18 | * @var \LaravelFly\WebSocketHandler\WebSocketHandlerInterface 19 | */ 20 | var $handler; 21 | 22 | public function __construct(array $options) 23 | { 24 | 25 | $this->parseOptions($options); 26 | 27 | $options['listen_ip'] = '0.0.0.0'; 28 | 29 | $this->server = $server = new \swoole_websocket_server($options['listen_ip'], $options['listen_port']); 30 | 31 | $server->set($options); 32 | } 33 | 34 | public function setListeners() 35 | { 36 | $this->server->on('open', array($this, 'onOpen')); 37 | $this->server->on('message', array($this, 'onMessage')); 38 | $this->server->on('close', array($this, 'onClose')); 39 | } 40 | 41 | function onOpen(\swoole_websocket_server $svr, \swoole_http_request $req) 42 | { 43 | var_dump($req); 44 | } 45 | 46 | 47 | } -------------------------------------------------------------------------------- /src/LaravelFly/Tinker/ClassAliasAutoloader.php: -------------------------------------------------------------------------------- 1 | shell = $shell; 22 | 23 | $vendorPath = dirname(dirname($classMapPath)); 24 | 25 | $classes = require $classMapPath; 26 | 27 | // $excludedAliases = collect(config('tinker.dont_alias', [])); 28 | $excludedAliases = collect([]); 29 | 30 | foreach ($classes as $class => $path) { 31 | if (! Str::contains($class, '\\') || Str::startsWith($path, $vendorPath)) { 32 | continue; 33 | } 34 | 35 | if (! $excludedAliases->filter(function ($alias) use ($class) { 36 | return Str::startsWith($class, $alias); 37 | })->isEmpty()) { 38 | continue; 39 | } 40 | 41 | $name = class_basename($class); 42 | 43 | if (! isset($this->classes[$name])) { 44 | $this->classes[$name] = $class; 45 | } 46 | } 47 | } 48 | 49 | function addClasses($array) 50 | { 51 | foreach ($array as $class) { 52 | 53 | $name = class_basename($class); 54 | 55 | if (!isset($this->classes[$name])) { 56 | $this->classes[$name] = $class; 57 | } 58 | } 59 | 60 | } 61 | 62 | static public function register(Shell $shell, $classMapPath) 63 | { 64 | if (!static::$registered) { 65 | 66 | $me = parent::register($shell, $classMapPath); 67 | 68 | static::$registered = true; 69 | 70 | return $me; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/LaravelFly/Tinker/Shell.php: -------------------------------------------------------------------------------- 1 | 'never', 55 | 'eraseDuplicates' => true, 56 | ]); 57 | 58 | $tk = new TinkerCommand(); 59 | 60 | $config->getPresenter()->addCasters( 61 | $tk->getCasters() 62 | ); 63 | 64 | static::$instance = $shell = new Shell($config); 65 | 66 | $config->setShell($shell); 67 | 68 | \Psy\info($config); 69 | 70 | $shell->server = $server; 71 | 72 | $commands = $shell->getDefaultCommands(); 73 | // this will overwrite Psy's WhereamiCommand 74 | $commands[] = new WhereamiCommand($config->colorMode()); 75 | 76 | $shell->addCommands($commands); 77 | //todo 78 | // $shell->setIncludes($this->argument('include')); 79 | 80 | $path = $shell->server->path('vendor/composer/autoload_classmap.php'); 81 | 82 | $shell->loader = ClassAliasAutoloader::register($shell, $path); 83 | 84 | $shell->addInput('whereami -n3', true); 85 | } 86 | 87 | static function addAlias($array) 88 | { 89 | $shell = static::$instance; 90 | $shell->loader->addClasses($array); 91 | } 92 | 93 | static function debug(array $vars = array(), $boundObject = null) 94 | { 95 | 96 | $shell = static::$instance; 97 | 98 | $shell->setScopeVariables($vars); 99 | 100 | 101 | if ($boundObject !== null) { 102 | $shell->setBoundObject($boundObject); 103 | } 104 | 105 | try { 106 | $shell->run(); 107 | } finally { 108 | //todo 109 | // $this->loader->unregister(); 110 | } 111 | return $shell->getScopeVariables(false); 112 | } 113 | 114 | } 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/LaravelFly/Tinker/TinkerCaster.php: -------------------------------------------------------------------------------- 1 | setting['worker_num']>1){ 68 | $results[Caster::PREFIX_VIRTUAL . '** TIP **'] = 'worker_id and worker_pid are of only the worker in which tinker() running'; 69 | } 70 | 71 | foreach (self::$swooleServerProperties as $prop) { 72 | $v = $server->$prop; 73 | 74 | if (is_null($v)) continue; 75 | 76 | $results[Caster::PREFIX_VIRTUAL . $prop] = $v; 77 | } 78 | 79 | return $results; 80 | 81 | } 82 | 83 | /** 84 | * Get an array representing the properties of a \Swoole\Server\Port. 85 | * 86 | * @param \Swoole\Server\Port 87 | * @return array 88 | */ 89 | public static function castSwooleServerPort($port) 90 | { 91 | $results = []; 92 | 93 | foreach (self::$swooleServerPortProperties as $property) { 94 | try { 95 | $val = $port->$property; 96 | 97 | if (!is_null($val)) { 98 | $results[Caster::PREFIX_VIRTUAL . $property] = $val; 99 | } 100 | } catch (Exception $e) { 101 | // 102 | } 103 | } 104 | 105 | return $results; 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/LaravelFly/Tinker/TinkerCommand.php: -------------------------------------------------------------------------------- 1 | isDebugCall($stackFrame)) { 37 | return $stackFrame; 38 | } 39 | } 40 | 41 | return end($backtrace); 42 | } 43 | 44 | protected static function isDebugCall(array $stackFrame) 45 | { 46 | $class = isset($stackFrame['class']) ? $stackFrame['class'] : null; 47 | $function = isset($stackFrame['function']) ? $stackFrame['function'] : null; 48 | 49 | return ($class === 'LaravelFly\Tinker\Shell' && in_array($function, array('__construct', 'debug'))) || 50 | ($class === null && $function === 'tinker'); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/LaravelFly/Tools/LaravelJobByTask/Connectors/TaskJobConnector.php: -------------------------------------------------------------------------------- 1 | swoole = $swoole; 26 | } 27 | 28 | /** 29 | * Establish a queue connection. 30 | * 31 | * @param array $config 32 | * @return \Illuminate\Contracts\Queue\Queue 33 | */ 34 | public function connect(array $config) 35 | { 36 | return new TaskJobQueue($this->swoole); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/LaravelFly/Tools/LaravelJobByTask/TaskJob.php: -------------------------------------------------------------------------------- 1 | container = $container; 62 | $this->swoole = $swoole; 63 | $this->jobObject = $job; 64 | $this->data = $data; 65 | $this->taskId = $taskId; 66 | $this->srcWorkderId = $srcWrokerId; 67 | } 68 | 69 | /** 70 | * Fire the job. 71 | * 72 | * @return void 73 | */ 74 | public function fire() 75 | { 76 | 77 | if ($this->data) { 78 | 79 | // vendor/laravel/framework/src/Illuminate/Events/CallQueuedListener::handle() 80 | call_user_func_array( 81 | [$this->jobObject, 'handle'], $this->data 82 | ); 83 | return; 84 | } 85 | 86 | $this->jobObject->handle(); 87 | 88 | // todo 89 | return; 90 | parent::fire(); 91 | } 92 | 93 | /** 94 | * Get the number of times the job has been attempted. 95 | * @return int 96 | */ 97 | public function attempts() 98 | { 99 | return ($this->job['attempts'] ?? null) + 1; 100 | } 101 | 102 | /** 103 | * Get the raw body string for the job. 104 | * @return string 105 | */ 106 | public function getRawBody() 107 | { 108 | return $this->job; 109 | } 110 | 111 | 112 | /** 113 | * Get the job identifier. 114 | * @return string 115 | */ 116 | public function getJobId() 117 | { 118 | return $this->taskId; 119 | } 120 | 121 | public function resolveName() 122 | { 123 | return get_class($this->jobObject); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/LaravelFly/Tools/LaravelJobByTask/TaskJobQueue.php: -------------------------------------------------------------------------------- 1 | swoole = $swoole; 29 | } 30 | 31 | function pushJobObject($job){ 32 | $this->jobs[] = $job; 33 | } 34 | 35 | /** 36 | * Push a new job onto the queue. 37 | * 38 | * @param string|object $job 39 | * @param mixed $data 40 | * @param string $queue 41 | * @return mixed 42 | */ 43 | public function push($job, $data = '', $queue = null) 44 | { 45 | return $this->pushRaw($this->createPayload($job, $data), $queue); 46 | } 47 | 48 | /** 49 | * Push a raw payload onto the queue. 50 | * 51 | * @param string $payload 52 | * @param string $queue 53 | * @param array $options 54 | * @return mixed 55 | */ 56 | public function pushRaw($payload, $queue = null, array $options = []) 57 | { 58 | return $this->swoole->task($payload, ! is_numeric($queue) ? 1 : (int) $queue); 59 | } 60 | 61 | /** 62 | * Push a new job onto the queue after a delay. 63 | * 64 | * @param \DateTimeInterface|\DateInterval|int $delay 65 | * @param string|object $job 66 | * @param mixed $data 67 | * @param string $queue 68 | * @return mixed 69 | */ 70 | public function later($delay, $job, $data = '', $queue = null) 71 | { 72 | return Timer::after($this->secondsUntil($delay) * 1000, function () use ($job, $data, $queue) { 73 | return $this->push($job, $data, $queue); 74 | }); 75 | } 76 | 77 | /** 78 | * Create a typical, string based queue payload array. 79 | * 80 | * @param string $job 81 | * @param mixed $data 82 | * 83 | * @throws Expcetion 84 | */ 85 | protected function createStringPayload($job, $data) 86 | { 87 | throw new Exception('Unsupported empty data'); 88 | } 89 | 90 | /** 91 | * Get the size of the queue. 92 | * 93 | * @param string $queue 94 | * @return int 95 | */ 96 | public function size($queue = null) 97 | { 98 | return -1; 99 | } 100 | 101 | /** 102 | * Pop the next job off of the queue. 103 | * 104 | * @param string $queue 105 | * @return \Illuminate\Contracts\Queue\Job|null 106 | */ 107 | public function pop($queue = null) 108 | { 109 | return array_shift($this->jobs); 110 | return null; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/LaravelFly/Tools/LaravelJobByTask/TaskJobServiceProvider.php: -------------------------------------------------------------------------------- 1 | oldDefault = $this->app['config']['queue.default']; 33 | $this->oldWorker = $this->app->make('queue.worker'); 34 | } 35 | 36 | $this->app['queue']->addConnector('swoole-task', function () { 37 | return new TaskJobConnector( 38 | // null? allow debugging in fpm 39 | method_exists($this->app, 'getSwoole') ? $this->app->getSwoole() : null 40 | ); 41 | }); 42 | 43 | 44 | $this->app['config']['queue.default'] = 'swoole-job'; 45 | 46 | $this->app['config']["queue.connections.swoole-job"] = 47 | $this->app['config']->get("laravelfly.swoole-job", []) + [ 48 | 'driver' => 'swoole-task', 49 | 'delay' => 0, 50 | 'force' => true, 51 | 'memory' => 128, 52 | 'timeout' => 60, 53 | 'tries' => 0, 54 | ]; 55 | 56 | 57 | // from: Illuminate\Queue\QueueServiceProvider::registerWorker 58 | $this->app->singleton('queue.worker', function () { 59 | return $this->taskWorker = new \LaravelFly\Tools\LaravelJobByTask\Worker( 60 | $this->app['queue'], $this->app['events'], $this->app[ExceptionHandler::class] 61 | ); 62 | }); 63 | } 64 | 65 | /** 66 | * @param bool $full whether restore default queue connection 67 | */ 68 | function restore($full = false) 69 | { 70 | if ($this->oldWorker) { 71 | 72 | $this->app['config']['queue.default'] = $full ? $this->oldDefault : 'sync'; 73 | 74 | $this->app->instance('queue.worker', $this->oldWorker); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/LaravelFly/Tools/LaravelJobByTask/Worker.php: -------------------------------------------------------------------------------- 1 | pop($queue))) { 28 | return $job; 29 | } 30 | // } 31 | } catch (Exception $e) { 32 | $this->exceptions->report($e); 33 | 34 | $this->stopWorkerIfLostConnection($e); 35 | 36 | $this->sleep(1); 37 | } catch (Throwable $e) { 38 | $this->exceptions->report($e = new FatalThrowableError($e)); 39 | 40 | $this->stopWorkerIfLostConnection($e); 41 | 42 | $this->sleep(1); 43 | } 44 | } 45 | 46 | public function daemon($connectionName, $queue, WorkerOptions $options) 47 | { 48 | 49 | // todo 50 | // if ($this->supportsAsyncSignals()) { 51 | // $this->listenForSignals(); 52 | // } 53 | 54 | $lastRestart = $this->getTimestampOfLastQueueRestart(); 55 | 56 | while (true) { 57 | 58 | $job = $this->getNextJob( 59 | $this->manager->connection($connectionName), $queue 60 | ); 61 | 62 | //todo 63 | // if ($this->supportsAsyncSignals()) { 64 | // $this->registerTimeoutHandler($job, $options); 65 | // } 66 | 67 | if ($job) { 68 | $this->runJob($job, $connectionName, $options); 69 | } else { 70 | return; 71 | } 72 | 73 | // Finally, we will check to see if we have exceeded our memory limits or if 74 | // the queue should restart based on other indications. If so, we'll stop 75 | // this worker and let whatever is "monitoring" it restart the process. 76 | $this->stopIfNecessary($options, $lastRestart); 77 | } 78 | } 79 | 80 | protected function stopIfNecessary(WorkerOptions $options, $lastRestart) 81 | { 82 | 83 | 84 | // if ($this->shouldQuit) { 85 | // $this->kill(); 86 | // } 87 | 88 | if ($this->memoryExceeded($options->memory)) { 89 | $this->stop(12); 90 | } 91 | // elseif ($this->queueShouldRestart($lastRestart)) { 92 | // $this->stop(); 93 | // } 94 | } 95 | 96 | public function stop($status = 0) 97 | { 98 | $this->events->dispatch(new \Illuminate\Queue\Events\WorkerStopping); 99 | 100 | // todo 101 | // exit($status); 102 | } 103 | 104 | // todo 105 | protected function failJob($connectionName, $job, $e) 106 | { 107 | return FailingJob::handle($connectionName, $job, $e); 108 | } 109 | } -------------------------------------------------------------------------------- /src/LaravelFly/Tools/SessionTable.php: -------------------------------------------------------------------------------- 1 | pipe = $pipe; 21 | $this->minutes = $min; 22 | } 23 | 24 | function notValid($lasttime) 25 | { 26 | return round(time() / 60) - $lasttime > $this->minutes; 27 | } 28 | function valid($lasttime) 29 | { 30 | return round(time() / 60) - $lasttime < $this->minutes; 31 | } 32 | 33 | public function load(array $data) 34 | { 35 | foreach ($data as $key => $row) { 36 | if ($this->valid($row['last_activity'])) 37 | $this->set($key, $row); 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/LaravelFly/Tools/Table.php: -------------------------------------------------------------------------------- 1 | pipe && $this->pipe->restore(); 17 | } 18 | 19 | public function dump() 20 | { 21 | $this->pipe && $this->pipe->dump(); 22 | } 23 | 24 | 25 | } -------------------------------------------------------------------------------- /src/LaravelFly/Tools/TablePipe/Pipe.php: -------------------------------------------------------------------------------- 1 | table = $table; 21 | $this->dumpFile = $file; 22 | } 23 | 24 | public function getDumpFile(): string 25 | { 26 | return $this->dumpFile; 27 | } 28 | 29 | function restore() 30 | { 31 | if (!$file = $this->dumpFile) { 32 | return; 33 | } 34 | 35 | if (!\file_exists($file)) { 36 | return; 37 | } 38 | 39 | $content = \file_get_contents($file); 40 | $this->table->load((array)\json_decode($content, true)); 41 | } 42 | 43 | function dump() 44 | { 45 | 46 | if (!$file = $this->dumpFile) { 47 | return; 48 | } 49 | 50 | $data = []; 51 | 52 | foreach ($this->table as $key => $row) { 53 | if ($this->table->valid($row['last_activity'])) 54 | $data[$key] = $row; 55 | } 56 | 57 | 58 | \file_put_contents($file, \json_encode($data)); 59 | } 60 | } -------------------------------------------------------------------------------- /src/LaravelFly/Tools/TablePipe/RedisPipe.php: -------------------------------------------------------------------------------- 1 | table = $table; 21 | 22 | $this->redis = app('redis')->connection(config('session.connection')); 23 | $this->prefix = config('cache.prefix'); 24 | } 25 | 26 | function restore() 27 | { 28 | // TODO: Implement export() method. 29 | 30 | return $this->redis->get($this->prefix.$sessionId) ?: ''; 31 | } 32 | 33 | function dump() 34 | { 35 | foreach ($this->table as $key => $row) { 36 | /** 37 | * @var \swoole_table_row $row 38 | */ 39 | $lasttime = $row['last_activity']; 40 | $used = round(time() / 60) - $lasttime; 41 | $remained = $this->handler->minutes - $used; 42 | if ($remained > 0) { 43 | 44 | $result = $this->redis->setEx($this->prefix . $key, $remained, $row['payload']); 45 | 46 | } 47 | 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/LaravelFly/WebSocketHandler/WebSocketHandlerInterface.php: -------------------------------------------------------------------------------- 1 | fly($callback, $write); 46 | } 47 | } 48 | 49 | } 50 | if (!function_exists('fly2')) { 51 | if (defined('LARAVELFLY_COROUTINE') && LARAVELFLY_COROUTINE) { 52 | function fly2($callback) 53 | { 54 | \fly($callback, true); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/DirTest.php: -------------------------------------------------------------------------------- 1 | getTest($testsDir, '', 'Test.php'); 17 | try { 18 | $testRunner->doRun($suit, ['stopOnError' => false], false); 19 | } catch (\ReflectionException $e) { 20 | echo $e->getMessage(); 21 | } 22 | $r = ob_get_clean(); 23 | // file_put_contents('/vagrant/test_ob_abc',$r); 24 | return $r; 25 | } 26 | 27 | function dirTestInProcess($init_func, $testsDir) 28 | { 29 | return self::process(function () use ($init_func, $testsDir) { 30 | $init_func(); 31 | return $this->dirTestInOb($testsDir); 32 | }); 33 | } 34 | 35 | protected function _includeFlyFiles($makeApp = false) 36 | { 37 | $constances = [ 38 | 'WORKER_COROUTINE_ID' => -1, 39 | 'LARAVELFLY_MODE' => 'Map', 40 | 'LARAVELFLY_SERVICES' => [ 41 | 'config' => false, 42 | 'kernel' => false, 43 | 'hash' => false, 44 | 'view.finder' => false, 45 | 46 | ] 47 | ]; 48 | foreach ($constances as $name => $val) { 49 | define($name, $val); 50 | } 51 | 52 | $options = ['mode' => 'Map', 'log_cache' => true,]; 53 | 54 | \LaravelFly\Server\Common::includeFlyFiles($options); 55 | 56 | if ($makeApp) { 57 | $commonServer = new \LaravelFly\Server\Common(); 58 | $commonServer->_makeLaravelApp(); 59 | $appR = new \ReflectionProperty(Common::class,'app'); 60 | $appR->setAccessible(true); 61 | $app = $appR->getValue($commonServer); 62 | return $app; 63 | } 64 | 65 | } 66 | 67 | // first include fly files, then do test 68 | function dirTestOnFlyFiles($testsDir, $makeApp = false) 69 | { 70 | self::assertTrue(is_dir($testsDir), "ensure $testsDir exists"); 71 | 72 | $init = function () use ($makeApp) { 73 | $this->_includeFlyFiles($makeApp); 74 | }; 75 | 76 | return $this->dirTestInProcess($init, $testsDir); 77 | 78 | } 79 | 80 | function dirTestOnFlyServer($testsDir) 81 | { 82 | 83 | self::assertTrue(is_dir($testsDir), "ensure $testsDir exists"); 84 | 85 | $init = function () { 86 | 87 | static::makeNewFlyServer([], [ 88 | 'worker_num' => 1, 89 | 'listen_port' => 9601, 90 | // EARLY 91 | 'early_laravel' => true 92 | ]); 93 | }; 94 | 95 | return $this->dirTestInProcess($init , $testsDir); 96 | 97 | 98 | } 99 | } -------------------------------------------------------------------------------- /tests/Map/Feature/Fly/StreamHandlerTest.php: -------------------------------------------------------------------------------- 1 | 1, 'pre_include' => false,], function ($server) use ($chan) { 19 | $dispatcher = $server->getDispatcher(); 20 | 21 | $dispatcher->addListener('worker.ready', function (GenericEvent $event) use ($chan) { 22 | function stripLogRecord($file) 23 | { 24 | $new = []; 25 | foreach (file($file) as $line) { 26 | $eles = mb_split('\s+', $line); 27 | $new[] = $eles[3] ?? "\n"; 28 | } 29 | return implode('', $new); 30 | } 31 | 32 | $app = $event['app']; 33 | $log = $app->make('log'); 34 | $logFile = $app->storagePath() . '/logs/laravel.log'; 35 | unlink($logFile); 36 | 37 | $i = 1; 38 | while ($i < 5) { 39 | $log->info($i); 40 | $i++; 41 | } 42 | $chan->push(stripLogRecord($logFile)); 43 | 44 | $log->info($i++); // 5 45 | 46 | $chan->push(stripLogRecord($logFile)); 47 | 48 | while ($i < 11) { 49 | $log->info($i); 50 | $i++; 51 | } 52 | $chan->push(stripLogRecord($logFile)); 53 | 54 | $log->info($i); // 11 55 | $chan->push(stripLogRecord($logFile)); 56 | 57 | 58 | }); 59 | 60 | $server->start(); 61 | 62 | }, 5); 63 | 64 | 65 | } 66 | 67 | function testLog() 68 | { 69 | $chan = static::$chan; 70 | // no write 71 | self::assertEquals("", $chan->pop()); 72 | // write , as $flyCacheMax == 5 73 | self::assertEquals("12345\n", $chan->pop()); 74 | self::assertEquals("12345\n678910\n", $chan->pop()); 75 | // no change, 11 is in cache 76 | self::assertEquals("12345\n678910\n", $chan->pop()); 77 | 78 | $logFile = static::$laravelAppRoot. '/storage/logs/laravel.log'; 79 | 80 | // after shutdown 81 | self::assertEquals("12345\n678910\n11\n", $this->stripLogRecord($logFile), 'after shutdown, see reason : 82 | // todo why? 83 | in src/fly/StreamHandler.php 84 | '); 85 | 86 | } 87 | 88 | /** 89 | * extract 9 from this string: 90 | * [2018-04-13 00:03:32] testing.INFO: 9 91 | * 92 | * @param $file 93 | * @return string 94 | */ 95 | function stripLogRecord($file) 96 | { 97 | $new = []; 98 | foreach (file($file) as $line) { 99 | $eles = mb_split('\s+', $line); 100 | $new[] = $eles[3] ?? "\n"; 101 | } 102 | return implode('', $new); 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /tests/Map/Feature/FlyOfficialFilesTest.php: -------------------------------------------------------------------------------- 1 | processGetArray(function () { 25 | return Common::getFlyMap(); 26 | }); 27 | 28 | $flyFilesNumber = 25; 29 | 30 | self::assertEquals($flyFilesNumber, count($map)); 31 | 32 | // +3: . an .. and FileViewFinderSameView.php 33 | // +1: 'Database/Eloquent/Concerns/HasRelationships.php' not used, but available 34 | // -4: 5 files in a dir ViewConcerns 35 | // -1: 2 files in a dir Database 36 | // -2: 3 files in a dir Foundation 37 | // -1: 2 files in a dir Routing 38 | // -1: Kernel.php 39 | $topNumber = $flyFilesNumber + 3 + 1 - 4 - 1 - 2 - 1 - 1; 40 | self::assertEquals($topNumber, count(scandir(static::$flyDir, SCANDIR_SORT_NONE))); 41 | 42 | // +3: another kernel.php whoses class is App\Http\Kernel.php 43 | // Http/ 44 | // extended/ 45 | // -1: FileViewFinderSameView.php 46 | self::assertEquals($topNumber + 3 - 1, count(scandir(static::$backOfficalDir, SCANDIR_SORT_NONE))); 47 | 48 | foreach ($map as $f => $originLocation) { 49 | 50 | self::assertEquals(true, is_file(static::$backOfficalDir . $f)); 51 | 52 | if ($f !== 'Http/Kernel.php') 53 | self::assertEquals(true, is_file(static::$flyDir . $f), static::$flyDir . $f); 54 | 55 | $ff = static::$workingRoot . $originLocation; 56 | self::assertEquals(true, is_file($ff), "is file: $ff"); 57 | } 58 | } 59 | 60 | function testCompareFilesContent() 61 | { 62 | $map = static::$map; 63 | $this->compareFilesContent($map); 64 | } 65 | 66 | 67 | /** 68 | * compare files of LaravelFly,not of Laravel 69 | */ 70 | function testCompareFilesContentLaravelFly() 71 | { 72 | 73 | $origin = [ 74 | static::$flyDir, 75 | static::$backOfficalDir, 76 | static::$laravelVersionAppRoot 77 | 78 | ]; 79 | static::$flyDir = FLY_ROOT . '/src/fly/'; 80 | static::$backOfficalDir = FLY_ROOT . '/tests/offcial_files/'; 81 | static::$laravelVersionAppRoot = static::$laravelAppRoot; 82 | 83 | 84 | $map = [ 85 | // Kernel with Dict version should go with Kernel without Dict 86 | 'Kernel.php' => '/vendor/scil/laravel-fly/src/LaravelFly/Map/Kernel.php', 87 | 88 | ]; 89 | $this->compareFilesContent($map); 90 | 91 | list( 92 | static::$flyDir, 93 | static::$backOfficalDir, 94 | static::$laravelVersionAppRoot 95 | ) = $origin; 96 | 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /tests/Map/LaravelTests/TestCase.php: -------------------------------------------------------------------------------- 1 | 'LaravelFly\Map\Illuminate\View\Factory', 20 | ]; 21 | 22 | static function setUpBeforeClass() 23 | { 24 | parent::setUpBeforeClass(); 25 | static::$laravelTestsDir = realpath( __DIR__ . '/../../../vendor/laravel/framework/tests'); 26 | } 27 | 28 | function testDir() 29 | { 30 | self::assertTrue(is_dir(static::$laravelTestsDir), static::$laravelTestsDir." not exists. \nmaybe need run\n: git clone -b 5.6 https://github.com/laravel/framework.git ". 31 | realpath(__DIR__.'/../../../vendor/laravel')); 32 | 33 | @mkdir(self::$tmpdir); 34 | self::assertTrue(is_dir(self::$tmpdir), "ensure /tmp/laravelfly_tests exists"); 35 | 36 | } 37 | 38 | function copyAndSed($subDir, $force=false) 39 | { 40 | 41 | $srcDir = static::$laravelTestsDir . $subDir; 42 | $testsBaseir = static::$tmpdir; 43 | $testsDir = $testsBaseir . $subDir; 44 | 45 | if(is_dir($testsDir) && !$force){ 46 | echo "[FLY WARN] use old dir $testsDir\n"; 47 | return $testsDir; 48 | } 49 | 50 | $cmd = "cp -f -r $srcDir $testsBaseir"; 51 | passthru($cmd, $r); 52 | if ($r !== 0) { 53 | self::fail("faild cmd: $cmd"); 54 | } 55 | 56 | $sedFile = __DIR__ . '/class_replace_sed.txt'; 57 | $cmd = "sed -i -f $sedFile `find $testsDir -type f `"; 58 | passthru($cmd, $r); 59 | if ($r !== 0) { 60 | self::fail("faild cmd: $cmd"); 61 | } 62 | 63 | return $testsDir; 64 | 65 | } 66 | 67 | private function testViewInWorker() 68 | { 69 | $testsDir = $this->copyAndSed('/View'); 70 | 71 | 72 | $r = $this->dirTestOnFlyServer($testsDir); 73 | 74 | echo "\n[[LARAVEL TEST RESULT]]\n", $r; 75 | 76 | sleep(2); 77 | 78 | } 79 | 80 | function testFoundationInWorker() 81 | { 82 | // $testsDir = $this->copyAndSed('/Foundation'); 83 | $testsDir = static::$laravelTestsDir . '/Foundation'; 84 | 85 | // only in ob 86 | // $r = $this->dirTestInOb($testsDir, true); 87 | // in process 88 | $r = $this->dirTestOnFlyFiles($testsDir); 89 | 90 | echo "\n[[LARAVEL TEST RESULT]]\n", $r; 91 | 92 | sleep(1); 93 | 94 | } 95 | 96 | function est1() 97 | { 98 | echo 'test:', self::process(function () { 99 | echo 33333333; 100 | }); 101 | } 102 | } -------------------------------------------------------------------------------- /tests/Map/LaravelTests/class_replace_sed.txt: -------------------------------------------------------------------------------- 1 | s/Illuminate\\View\\Factory/LaravelFly\\Map\\Illuminate\\View\\Factory/ 2 | -------------------------------------------------------------------------------- /tests/Map/Unit/FlyTest.php: -------------------------------------------------------------------------------- 1 | setAccessible(true); 19 | $initEnv->invoke(null, []); 20 | 21 | $r[] = defined('WORKER_COROUTINE_ID'); 22 | $r[] = WORKER_COROUTINE_ID; 23 | $r[] = function_exists('tinker'); 24 | 25 | return $r; 26 | }); 27 | 28 | self::assertEquals([false, true, 1, true], json_decode($r)); 29 | 30 | } 31 | 32 | function testEarly_laravel() 33 | { 34 | $r = self::process(function () { 35 | $r[] = defined('WORKER_COROUTINE_ID '); 36 | 37 | $initEnv = new \ReflectionMethod('\LaravelFly\Fly', 'initEnv'); 38 | $initEnv->setAccessible(true); 39 | $initEnv->invoke(null, ['early_laravel'=>true]); 40 | 41 | $r[] = defined('WORKER_COROUTINE_ID'); 42 | $r[] = WORKER_COROUTINE_ID; 43 | $r[] = function_exists('tinker'); 44 | 45 | return $r; 46 | }); 47 | 48 | self::assertEquals([false, true, 1, true], json_decode($r)); 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /tests/Map/Unit/Illuminate/Translation/TranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertResponsePassingRoutes( 17 | [ 18 | [ 19 | 'get', 20 | static::testBaseUrl . 'test1', 21 | function () { 22 | \Log::info( 'cid is '. \Co::getUid()); 23 | $configLocale = \App::getLocale(); 24 | $transLocale = app('translator')->getLocale(); 25 | $newLocale = 'en'; 26 | \App::setLocale($newLocale); 27 | $configLocale2 = \App::getLocale(); 28 | $transLocale2 = app('translator')->getLocale(); 29 | return "config: $configLocale -> $configLocale2; trans: $transLocale -> $transLocale2"; 30 | } 31 | ], 32 | [ 33 | 'get', 34 | static::testBaseUrl . 'test2', 35 | function () { 36 | \Co::sleep(2); 37 | $configLocale = \App::getLocale(); 38 | $transLocale = app('translator')->getLocale(); 39 | return "config: $configLocale; trans: $transLocale"; 40 | } 41 | ], 42 | ], 43 | [ 44 | static::testCurlBaseUrl . 'test1', 45 | static::testCurlBaseUrl . 'test2', 46 | ], 47 | [ 48 | "config: $default_locale -> en; trans: $default_locale -> en", 49 | "config: $default_locale; trans: $default_locale" 50 | ], 51 | function () use($default_locale) { 52 | app()->setLocale($default_locale); 53 | } 54 | ); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Map/Unit/Server/HttpServerTest.php: -------------------------------------------------------------------------------- 1 | assertResponsePassingRoutes( 15 | [ 16 | [ 17 | 'get', 18 | static::testBaseUrl . 'test1', 19 | function () { 20 | return \Request::path(); 21 | } 22 | ], 23 | [ 24 | 'get', 25 | static::testBaseUrl . 'test2', 26 | function () { 27 | return \Request::query('name'); 28 | } 29 | ], 30 | ], 31 | [ 32 | static::testCurlBaseUrl . 'test1', 33 | static::testCurlBaseUrl . 'test2?name=scil', 34 | ], 35 | [ 36 | 'laravelfly-test/test1', 37 | 'scil' 38 | ] 39 | ); 40 | 41 | } 42 | 43 | function testonOnRequestPost() 44 | { 45 | $this->assertResponsePassingRoutes( 46 | [ 47 | [ 48 | 'post', 49 | static::testBaseUrl . 'test1', 50 | function () { 51 | return \Request::path(); 52 | } 53 | ], 54 | [ 55 | 'post', 56 | static::testBaseUrl . 'test2', 57 | function () { 58 | return \Request::query('name'); 59 | } 60 | ], 61 | ], 62 | [ 63 | [ 64 | 'url' => static::testCurlBaseUrl . 'test1', 65 | 'options' => [ 66 | CURLOPT_POST => 1, 67 | ] 68 | ], 69 | [ 70 | 'url' => static::testCurlBaseUrl . 'test2?name=scil', 71 | 'options' => [ 72 | CURLOPT_POST => 1, 73 | CURLOPT_POSTFIELDS => ['name' => 'scil', 'password' => 'passuser1', 'gender' => 1], 74 | ] 75 | ] 76 | ], 77 | [ 78 | 'laravelfly-test/test1', 79 | 'scil' 80 | ] 81 | ); 82 | 83 | } 84 | 85 | function testHeader() 86 | { 87 | $this->assertResponsePassingRoutes( 88 | [ 89 | [ 90 | 'get', 91 | static::testBaseUrl . 'test1', 92 | function () { 93 | return \Request::header('accept'); 94 | } 95 | ], 96 | ], 97 | [ 98 | [ 99 | 'url' => static::testCurlBaseUrl . 'test1', 100 | 'options' => [ 101 | ] 102 | ] 103 | ], 104 | [ 105 | '*/*', 106 | ] 107 | ); 108 | 109 | } 110 | } -------------------------------------------------------------------------------- /tests/Map/Unit/Server/IncludeFlyFilesTest.php: -------------------------------------------------------------------------------- 1 | 'Map', 'log_cache' => 2]; 18 | 19 | \LaravelFly\Server\Common::$flyBaseDir = LARAVEL_APP_ROOT. '/vendor/scil/laravel-fly-files/src/'; 20 | \LaravelFly\Server\Common::includeFlyFiles($options); 21 | 22 | 23 | $r[] = class_exists('Illuminate\Foundation\Application', false); 24 | return $r; 25 | }); 26 | 27 | self::assertEquals([false,false,true], $r); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /tests/Map/Unit/Server/Traits/WorkerTest.php: -------------------------------------------------------------------------------- 1 | 2, 27 | 'mode' => 'Map', 28 | 'listen_port' => 9503, 29 | 'daemonize' => false, 30 | 'log_file' => $appRoot . '/storage/logs/swoole.log', 31 | 'pre_include' => false, 32 | 'watch_down' => true, 33 | ]; 34 | 35 | $mychan = static::$chan = new \Swoole\Channel(1024 * 256); 36 | 37 | $r = self::createFlyServerInProcess($constances,$options, function (HttpServer $server) use ($appRoot, $options, $mychan) { 38 | 39 | 40 | $dispatcher = $server->getDispatcher(); 41 | 42 | // use assert in server, these tests can not reported by phpunit , but if assert failed, error output in console 43 | $dispatcher->addListener('worker.ready', function (GenericEvent $event) use ($server, $appRoot, $mychan) { 44 | 45 | $app = $event['app']; 46 | 47 | @unlink($server->getDownFileDir() . '/down'); 48 | 49 | // self::assertEquals(0, $app->isDownForMaintenance()); 50 | $r[] = $app->isDownForMaintenance(); 51 | 52 | /** 53 | * worker 0 used for watchDownFile, worker 1 used for phpunit 54 | * 55 | * if only one worker,closure added by swoole_event_add in watchDownFile() only run 56 | * after the closure here finish 57 | * $server->getIntegerMemory('isDown') will not change. 58 | * 59 | */ 60 | if ($event['workerid'] === 0) return; 61 | 62 | // self::assertEquals(0, $server->getIntegerMemory('isDown')); 63 | $r[] = $server->getIntegerMemory('isDown'); 64 | 65 | passthru("cd $appRoot && php artisan down"); 66 | sleep(1); 67 | // self::assertEquals(1, $app->isDownForMaintenance()); 68 | $r[] = $app->isDownForMaintenance(); 69 | 70 | // file_put_contents($server->path('storage/framework/ok3'), $server->getIntegerMemory('isDown')); 71 | // self::assertEquals(1, $server->getIntegerMemory('isDown')); 72 | $r[] = $server->getIntegerMemory('isDown'); 73 | 74 | 75 | passthru("cd $appRoot && php artisan up"); 76 | sleep(2); 77 | // self::assertEquals(0, $app->isDownForMaintenance()); 78 | $r[] = $app->isDownForMaintenance(); 79 | 80 | sleep(1); 81 | // self::assertEquals(0, $server->getIntegerMemory('isDown')); 82 | $r[] = $server->getIntegerMemory('isDown'); 83 | 84 | 85 | $mychan->push(json_encode($r)); 86 | 87 | 88 | }); 89 | 90 | $server->start(); 91 | 92 | }, 10); 93 | 94 | $rr = json_decode($mychan->pop()); 95 | // var_dump("RRR:"); var_dump($rr); 96 | self::assertEquals([0, 0, 1, 1, 0, 0], $rr); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | addPsr4("Illuminate\\Tests\\", LARAVEL_APP_ROOT . "/vendor/laravel/framework/tests/"); 50 | 51 | 52 | require_once __DIR__ . "/swoole_src_tests/include/swoole.inc"; 53 | require_once __DIR__ . "/swoole_src_tests/include/lib/curl_concurrency.php"; 54 | -------------------------------------------------------------------------------- /tests/swoole_src_tests/include/bootstrap.php: -------------------------------------------------------------------------------- 1 | SWOOLE_LOG_INFO, 14 | 'trace_flags' => 0, 15 | 'socket_timeout' => 5, 16 | ]); 17 | } 18 | 19 | 20 | require_once __DIR__ . '/swoole.inc'; -------------------------------------------------------------------------------- /tests/swoole_src_tests/include/config.php: -------------------------------------------------------------------------------- 1 | 'http://g.cn', 8 | * 'options'=>[ 9 | * CURLOPT_HEADER => 1 10 | * ] 11 | * ]) 12 | * @author: scil 13 | */ 14 | function curl_concurrency($arrayUrls) 15 | { 16 | $mh = curl_multi_init(); 17 | 18 | foreach ($arrayUrls as $i => $url) { 19 | $options = []; 20 | 21 | if (is_array($url)) { 22 | $options = $url['options'] ?? []; 23 | $url = $url['url']; 24 | } 25 | 26 | 27 | $conn[$i] = curl_init($url); 28 | curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1); 29 | 30 | curl_setopt_array($conn[$i], $options); 31 | 32 | curl_multi_add_handle($mh, $conn[$i]); 33 | } 34 | 35 | $active = null; 36 | 37 | do { 38 | $mrc = curl_multi_exec($mh, $active); 39 | } while ($mrc == CURLM_CALL_MULTI_PERFORM); 40 | 41 | while ($active && $mrc == CURLM_OK) { 42 | if (curl_multi_select($mh) != -1) { 43 | usleep(100); 44 | } 45 | do { 46 | $mrc = curl_multi_exec($mh, $active); 47 | } while ($mrc == CURLM_CALL_MULTI_PERFORM); 48 | 49 | } 50 | 51 | 52 | foreach ($arrayUrls as $i => $url) { 53 | $res[$i] = curl_multi_getcontent($conn[$i]); 54 | curl_multi_remove_handle($mh, $conn[$i]); 55 | curl_close($conn[$i]); 56 | } 57 | 58 | curl_multi_close($mh); 59 | 60 | return $res; 61 | } 62 | -------------------------------------------------------------------------------- /tests/swoole_src_tests/include/swoole.inc: -------------------------------------------------------------------------------- 1 | 1, 10 | "thread_num" => 1, 11 | 'disable_dns_cache' => true, 12 | 'dns_lookup_random' => true, 13 | ]); -------------------------------------------------------------------------------- /tests/swoole_src_tests/include/toolkit/TcpStat.php: -------------------------------------------------------------------------------- 1 | "ESTABLISHED", 8 | "syn-sent" => "SYN_SENT", 9 | "syn-recv" => "SYN_RCVD", 10 | "fin-wait-1" => "FIN_WAIT_1", 11 | "fin-wait-2" => "FIN_WAIT_2", 12 | "time-wait" => "TIME_WAIT", 13 | "closed" => "CLOSED", 14 | "close-wait" => "CLOSE_WAIT", 15 | "last-ack" => "LAST_ACK", 16 | "listen" => "LISTEN", 17 | "closing" => "CLOSING", 18 | ]; 19 | 20 | public static function xCount($path) 21 | { 22 | if (PHP_OS === "Darwin") { 23 | $n = `netstat -x | grep $path | wc -l`; 24 | return intval(trim($n)); 25 | } else { 26 | $n = `ss -x src $path | wc -l`; 27 | return intval(trim($n)) - 1; 28 | } 29 | } 30 | 31 | public static function count($host, $port, $states = ["established", "time-wait", "close-wait"]) { 32 | if (!ip2long($host)) { 33 | $host = gethostbyname($host); 34 | } 35 | 36 | $pipe = "wc -l"; 37 | $func = PHP_OS === "Darwin" ? "netstat" : "ss"; 38 | $states = static::fmtTcpState($states, $func); 39 | 40 | $info = []; 41 | foreach ($states as $state) { 42 | $ret = call_user_func([static::class, $func], $host, $port, $state, $pipe); 43 | $info[$state] = intval(trim($ret)) - 1; 44 | } 45 | 46 | return $info; 47 | } 48 | 49 | private static function netstat($host, $port, $state, $pipe = "") 50 | { 51 | if ($pipe) { 52 | $pipe = " | $pipe"; 53 | } 54 | // $4 src $5 dst $6 stats 55 | return `netstat -an | awk '(\$5 == "$host.$port" && \$6 == "$state") || NR==2 {print \$0}' $pipe`; 56 | } 57 | 58 | private static function ss($host, $port, $state, $pipe = "") 59 | { 60 | if ($pipe) { 61 | $pipe = " | $pipe"; 62 | } 63 | return `ss state $state dst $host:$port $pipe`; 64 | } 65 | 66 | private static function fmtTcpState(array $states, $type) 67 | { 68 | $from = $to = []; 69 | if ($type === "ss") { 70 | $to = static::SS_NETSTAT_TCP_STATE_MAP; 71 | $from = array_flip($to); 72 | } else if ($type === "netstat") { 73 | $from = static::SS_NETSTAT_TCP_STATE_MAP; 74 | $to = array_flip($from); 75 | } 76 | 77 | $ret = []; 78 | foreach ($states as $state) { 79 | if (isset($to[$state])) { 80 | $ret[] = $state; 81 | } else if (isset($from[$state])) { 82 | $ret[] = $from[$state]; 83 | } 84 | } 85 | return $ret; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/swoole_src_tests/readme.md: -------------------------------------------------------------------------------- 1 | copy of part of swoole-src/tests/include 2 | plus 3 | include/lib/curl_concurrency.php writen by scil 4 | --------------------------------------------------------------------------------