webman env配置文件加载分析


webman env配置文件加载分析


正文

webman env配置文件加载,使用的包是 vlucas/phpdotenv,Laravel 也使用的这个包加载env配置文件。

获取配置使用的是包 illuminate/support,Laravel 也使用的这个包。

webman安装

composer 安装 webman:

composer create-project workerman/webman

root@02891538d8c9:/var/www/html# composer create-project workerman/webman webman_v1.3.7
Creating a "workerman/webman" project at "./webman_v1.3.7"
Installing workerman/webman (v1.3.7)
  - Installing workerman/webman (v1.3.7): Extracting archive
Created project in /var/www/html/webman_v1.3.7
Loading composer repositories with package information
Updating dependencies
Lock file operations: 6 installs, 0 updates, 0 removals
  - Locking monolog/monolog (2.4.0)
  - Locking nikic/fast-route (v1.3.0)
  - Locking psr/container (2.0.2)
  - Locking psr/log (1.1.4)
  - Locking workerman/webman-framework (v1.3.12)
  - Locking workerman/workerman (v4.0.36)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Installing psr/log (1.1.4): Extracting archive
  - Installing monolog/monolog (2.4.0): Extracting archive
  - Installing workerman/workerman (v4.0.36): Extracting archive
  - Installing psr/container (2.0.2): Extracting archive
  - Installing nikic/fast-route (v1.3.0): Extracting archive
  - Installing workerman/webman-framework (v1.3.12): Extracting archive
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
Create start.php
Create support/bootstrap.php
Create support/Plugin.php
16 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
3 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
root@02891538d8c9:/var/www/html#
root@02891538d8c9:/var/www/html# cd webman_v1.3.7/

vlucas/phpdotenv 包写入项目 composer.json 文件:

{
  "name": "workerman/webman",
  "type": "project",
  "keywords": [
    "high performance",
    "http service"
  ],
  "homepage": "http://www.workerman.net",
  "license": "MIT",
  "description": "High performance HTTP Service Framework.",
  "authors": [
    {
      "name": "walkor",
      "email": "walkor@workerman.net",
      "homepage": "http://www.workerman.net",
      "role": "Developer"
    }
  ],
  "support": {
    "email": "walkor@workerman.net",
    "issues": "https://github.com/walkor/webman/issues",
    "forum": "http://wenda.workerman.net/",
    "wiki": "http://workerman.net/doc/webman",
    "source": "https://github.com/walkor/webman"
  },
  "require": {
    "php": ">=7.2",
    "workerman/webman-framework": "^1.3.12",
    "monolog/monolog": "^2.0",
    "vlucas/phpdotenv": "^5.4"
  },
  "suggest": {
    "ext-event": "For better performance. "
  },
  "autoload": {
    "psr-4": {
      "": "./",
      "App\\": "./app"
    },
    "files": [
      "./support/helpers.php"
    ]
  },
  "scripts": {
    "post-package-install": [
      "support\\Plugin::install"
    ],
    "post-package-update": [
      "support\\Plugin::install"
    ],
    "pre-package-uninstall": [
      "support\\Plugin::uninstall"
    ]
  }
}

安装 vlucas/phpdotenv 包:

composer update vlucas/phpdotenv

root@02891538d8c9:/var/www/html/webman_v1.3.7# composer install
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`.
Nothing to install, update or remove
Generating autoload files
3 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7# composer update vlucas/phpdotenv
Loading composer repositories with package information
Updating dependencies
Lock file operations: 6 installs, 0 updates, 0 removals
  - Locking graham-campbell/result-type (v1.0.4)
  - Locking phpoption/phpoption (1.8.1)
  - Locking symfony/polyfill-ctype (v1.25.0)
  - Locking symfony/polyfill-mbstring (v1.25.0)
  - Locking symfony/polyfill-php80 (v1.25.0)
  - Locking vlucas/phpdotenv (v5.4.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Downloading symfony/polyfill-php80 (v1.25.0)
  - Installing symfony/polyfill-php80 (v1.25.0): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.25.0): Extracting archive
  - Installing symfony/polyfill-ctype (v1.25.0): Extracting archive
  - Installing phpoption/phpoption (1.8.1): Extracting archive
  - Installing graham-campbell/result-type (v1.0.4): Extracting archive
  - Installing vlucas/phpdotenv (v5.4.1): Extracting archive
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
Generating autoload files
9 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
root@02891538d8c9:/var/www/html/webman_v1.3.7#

项目根目录下的.env 文件中写入配置内容,如:

# 数据库配置
database.hostname = 127.0.0.1
database.database = dataku
database.username = xyz
database.password = abc123
database.hostport = 3306

webman启动

webman启动:

php start.php start

root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7# php start.php start
Workerman[start.php] start in DEBUG mode
----------------------------------------- WORKERMAN -----------------------------------------
Workerman version:4.0.36          PHP version:7.4.28
------------------------------------------ WORKERS ------------------------------------------
proto   user            worker          listen                 processes    status
tcp     root            webman          http://0.0.0.0:8787    8             [OK]
tcp     root            monitor         none                   1             [OK]
---------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
^CWorkerman[start.php] stopping ...
Workerman[start.php] has been stopped
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7# php start.php stop
Workerman[start.php] stop
Workerman[start.php] not run
root@02891538d8c9:/var/www/html/webman_v1.3.7#

.env加载分析

start.php 内容:

#!/usr/bin/env php
<?php
require_once __DIR__ . '/vendor/autoload.php';

use Workerman\Worker;
use Workerman\Protocols\Http;
use Workerman\Connection\TcpConnection;
use Webman\App;
use Webman\Config;
use Webman\Route;
use Webman\Middleware;
use Dotenv\Dotenv;
use support\Request;
use support\Log;
use support\Container;

ini_set('display_errors', 'on');
error_reporting(E_ALL);

if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
    if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
        Dotenv::createUnsafeImmutable(base_path())->load();
    } else {
        Dotenv::createMutable(base_path())->load();
    }
}

Config::load(config_path(), ['route', 'container']);

if ($timezone = config('app.default_timezone')) {
    date_default_timezone_set($timezone);
}

$runtime_logs_path = runtime_path() . DIRECTORY_SEPARATOR . 'logs';
if ( !file_exists($runtime_logs_path) || !is_dir($runtime_logs_path) ) {
    if (!mkdir($runtime_logs_path,0777,true)) {
        throw new \RuntimeException("Failed to create runtime logs directory. Please check the permission.");
    }
}

$runtime_views_path = runtime_path() . DIRECTORY_SEPARATOR . 'views';
if ( !file_exists($runtime_views_path) || !is_dir($runtime_views_path) ) {
    if (!mkdir($runtime_views_path,0777,true)) {
        throw new \RuntimeException("Failed to create runtime views directory. Please check the permission.");
    }
}

Worker::$onMasterReload = function () {
    if (function_exists('opcache_get_status')) {
        if ($status = opcache_get_status()) {
            if (isset($status['scripts']) && $scripts = $status['scripts']) {
                foreach (array_keys($scripts) as $file) {
                    opcache_invalidate($file, true);
                }
            }
        }
    }
};

$config = config('server');
Worker::$pidFile = $config['pid_file'];
Worker::$stdoutFile = $config['stdout_file'];
Worker::$logFile = $config['log_file'];
Worker::$eventLoopClass = $config['event_loop'] ?? '';
TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10 * 1024 * 1024;
if (property_exists(Worker::class, 'statusFile')) {
    Worker::$statusFile = $config['status_file'] ?? '';
}
if (property_exists(Worker::class, 'stopTimeout')) {
    Worker::$stopTimeout = $config['stop_timeout'] ?? 2;
}

if ($config['listen']) {
    $worker = new Worker($config['listen'], $config['context']);
    $property_map = [
        'name',
        'count',
        'user',
        'group',
        'reusePort',
        'transport',
        'protocol'
    ];
    foreach ($property_map as $property) {
        if (isset($config[$property])) {
            $worker->$property = $config[$property];
        }
    }

    $worker->onWorkerStart = function ($worker) {
        require_once base_path() . '/support/bootstrap.php';
        $app = new App($worker, Container::instance(), Log::channel('default'), app_path(), public_path());
        Http::requestClass(config('app.request_class', config('server.request_class', Request::class)));
        $worker->onMessage = [$app, 'onMessage'];
    };
}

// Windows does not support custom processes.
if (\DIRECTORY_SEPARATOR === '/') {
    foreach (config('process', []) as $process_name => $config) {
        worker_start($process_name, $config);
    }
    foreach (config('plugin', []) as $firm => $projects) {
        foreach ($projects as $name => $project) {
            foreach ($project['process'] ?? [] as $process_name => $config) {
                worker_start("plugin.$firm.$name.$process_name", $config);
            }
        }
    }
}

Worker::runAll();

里面这一段,就是加载 .env 配置:

if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
    if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
        Dotenv::createUnsafeImmutable(base_path())->load();
    } else {
        Dotenv::createMutable(base_path())->load();
    }
}

上面两个方法的内容:

<?php
use Dotenv\Repository\RepositoryBuilder;

class Dotenv
{
    public static function createUnsafeImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()
            ->addAdapter(PutenvAdapter::class)
            ->immutable()
            ->make();
    
        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }
    
    public static function createMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()->make();
    
        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }
    
    // 省略若干...
}

获取分析

在项目代码中获取环境配置,如:

$host = env('database.hostname', '127.0.0.1');

使用包 illuminate/support ,好多包依赖于这个包,如 illuminate/busilluminate/databaseilluminate/eventsilluminate/eventsilluminate/paginationilluminate/pipelineilluminate/redis 等。

项目 composer.json 文件 中增加 psr/containerilluminate/support

  "require": {
    "php": ">=7.2",
    "workerman/webman-framework": "^1.3.12",
    "monolog/monolog": "^2.0",
    "vlucas/phpdotenv": "^5.4",
    "psr/container": "^1.0",
    "illuminate/support": "^8.83"
  },

安装包:

composer update psr/container

composer update illuminate/support

root@02891538d8c9:/var/www/html/webman_v1.3.7# composer update illuminate/support
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - illuminate/contracts[v8.0.0, ..., v8.83.11] require psr/container ^1.0 -> found psr/contaut the package is fixed to 2.0.2 (lock file version) by a partial update and that version does s an argument for the update command.
    - illuminate/support[v8.83.0, ..., v8.83.11] require illuminate/contracts ^8.0 -> satisfiab ..., v8.83.11].
    - Root composer.json requires illuminate/support ^8.83 -> satisfiable by illuminate/support

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packversions.
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7# composer update psr/container
Loading composer repositories with package information

Updating dependencies
Lock file operations: 11 installs, 1 update, 0 removals
  - Locking doctrine/inflector (2.0.4)
  - Locking illuminate/collections (v8.83.11)
  - Locking illuminate/contracts (v8.83.11)
  - Locking illuminate/macroable (v8.83.11)
  - Locking illuminate/support (v8.83.0)
  - Locking nesbot/carbon (2.58.0)
  - Downgrading psr/container (2.0.2 => 1.1.2)
  - Locking psr/simple-cache (1.0.1)
  - Locking symfony/deprecation-contracts (v2.5.1)
  - Locking symfony/translation (v5.4.8)
  - Locking symfony/translation-contracts (v2.5.1)
  - Locking voku/portable-ascii (1.6.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 11 installs, 1 update, 0 removals
  - Downloading symfony/translation-contracts (v2.5.1)
  - Downloading symfony/deprecation-contracts (v2.5.1)
  - Downloading symfony/translation (v5.4.8)
  - Downloading nesbot/carbon (2.58.0)
  - Downloading illuminate/collections (v8.83.11)
  - Installing voku/portable-ascii (1.6.1): Extracting archive
  - Installing symfony/translation-contracts (v2.5.1): Extracting archive
  - Installing symfony/deprecation-contracts (v2.5.1): Extracting archive
  - Installing symfony/translation (v5.4.8): Extracting archive
  - Installing nesbot/carbon (2.58.0): Extracting archive
  - Installing illuminate/macroable (v8.83.11): Extracting archive
  - Installing psr/simple-cache (1.0.1): Extracting archive
  - Downgrading psr/container (2.0.2 => 1.1.2): Extracting archive
  - Installing illuminate/contracts (v8.83.11): Extracting archive
  - Installing illuminate/collections (v8.83.11): Extracting archive
  - Installing doctrine/inflector (2.0.4): Extracting archive
  - Installing illuminate/support (v8.83.0): Extracting archive
 11/12 [=========================>--]  91%    Skipped installation of bin bin/carbon for package nesbot/carbon: file not found in package
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
> support\Plugin::install
9 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
15 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7# composer update illuminate/support
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
  - Upgrading illuminate/support (v8.83.0 => v8.83.11)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 1 update, 0 removals
  - Downloading illuminate/support (v8.83.11)
  - Upgrading illuminate/support (v8.83.0 => v8.83.11): Extracting archive
> support\Plugin::install
Generating autoload files
15 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
root@02891538d8c9:/var/www/html/webman_v1.3.7#
root@02891538d8c9:/var/www/html/webman_v1.3.7#

./vendor/illuminate/support/helpers.php 文件中有:

<?php

use Illuminate\Contracts\Support\DeferringDisplayableValue;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Arr;
use Illuminate\Support\Env;
use Illuminate\Support\HigherOrderTapProxy;
use Illuminate\Support\Optional;

// 省略若干

if (! function_exists('env')) {
    /**
     * Gets the value of an environment variable.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    function env($key, $default = null)
    {
        return Env::get($key, $default);
    }
}

// 省略若干

./vendor/illuminate/support/Env.php 文件中有:

<?php

namespace Illuminate\Support;

use Dotenv\Repository\Adapter\PutenvAdapter;
use Dotenv\Repository\RepositoryBuilder;
use PhpOption\Option;

class Env
{
    /**
     * Indicates if the putenv adapter is enabled.
     *
     * @var bool
     */
    protected static $putenv = true;

    /**
     * The environment repository instance.
     *
     * @var \Dotenv\Repository\RepositoryInterface|null
     */
    protected static $repository;

    /**
     * Enable the putenv adapter.
     *
     * @return void
     */
    public static function enablePutenv()
    {
        static::$putenv = true;
        static::$repository = null;
    }

    /**
     * Disable the putenv adapter.
     *
     * @return void
     */
    public static function disablePutenv()
    {
        static::$putenv = false;
        static::$repository = null;
    }

    /**
     * Get the environment repository instance.
     *
     * @return \Dotenv\Repository\RepositoryInterface
     */
    public static function getRepository()
    {
        if (static::$repository === null) {
            $builder = RepositoryBuilder::createWithDefaultAdapters();

            if (static::$putenv) {
                $builder = $builder->addAdapter(PutenvAdapter::class);
            }

            static::$repository = $builder->immutable()->make();
        }

        return static::$repository;
    }

    /**
     * Gets the value of an environment variable.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public static function get($key, $default = null)
    {
        return Option::fromValue(static::getRepository()->get($key))
            ->map(function ($value) {
                switch (strtolower($value)) {
                    case 'true':
                    case '(true)':
                        return true;
                    case 'false':
                    case '(false)':
                        return false;
                    case 'empty':
                    case '(empty)':
                        return '';
                    case 'null':
                    case '(null)':
                        return;
                }

                if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
                    return $matches[2];
                }

                return $value;
            })
            ->getOrCall(function () use ($default) {
                return value($default);
            });
    }
}

在这里看到了类 Dotenv\Repository\RepositoryBuilder,这个类是 vlucas/phpdotenv 包中的类。

总体追溯下来这个过程挺复杂,点到为止,有时间慢慢研究。

现在要说的是新版webma取消了对env的支持,因为phpdotenv 在多线程环境下有bug。






参考资料

webman 安装 https://www.workerman.net/doc/webman/install.html

vlucas/phpdotenv https://packagist.org/packages/vlucas/phpdotenv

vlucas/phpdotenv https://github.com/vlucas/phpdotenv

新版webman,取消env的原因是什么? https://www.workerman.net/q/7534

laravel的vlucas/phpdotenv源码解读 https://blog.csdn.net/linqing727/article/details/81986894


返回