百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Lumen框架加载env过程详解(lumen php框架)

ccwgpt 2024-10-15 09:09 32 浏览 0 评论

Lumen框架加载env的过程可以分为三步,第一步、读取env,第二步、将env数据放在系统环境变量中,第三步、释放环境变量

第一步、读取env

从程序入口中的index.php中可以看到,在lumen框架启动的时候先包含了bootstrap.php文件,而在bootstrap.php文件中完成了对env文件的读取。整个过程可以描述为index.php->bootstrap.php。


Dotenv源码分析

dotenv是一个第三方的扩展包,从目前的功能来看应该就是做env的load操作。可以在composer.json里面看到确实也引入了它。在vendor包中,可以看到他的源码目录结构,文件很少。看起来功能也没多少。我们可以顺着它去找一下最终的核心源码。

在Loader.php中可以看到主要的业务逻辑代码,基本的逻辑过程可以描述为这样:首先检测env文件是否存在,如果不存在,则抛出InvalidPathException异常。然后去调用readLinesFromFile方法去读取文件。readLinesFromFile就是lumen读取env文件的核心方法。

public function load()
    {
        $this->ensureFileIsReadable();

        $filePath = $this->filePath;
        $lines = $this->readLinesFromFile($filePath);

        foreach ($lines as $line) {
            if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
                $this->setEnvironmentVariable($line);
            }
        }
        return $lines;
    }
protected function readLinesFromFile($filePath)
    {
        // Read file into an array of lines with auto-detected line endings
        $autodetect = ini_get('auto_detect_line_endings');
        ini_set('auto_detect_line_endings', '1');
        $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        ini_set('auto_detect_line_endings', $autodetect);
        return $lines;
    }
  • readLinesFromFile方法分析

第一步、是获取php文件分隔符的配置。

第二步、将文件分隔符的值设置为1。

第三步、通过PHP原生函数file将env的内容读取到一个数组中。

第四步、将PHP文件分割符配置恢复。

第五步、将数据返回。

现在有几个问题需要解释一下。1.为什么要获取文件分割符然后又恢复。2.文件分隔符配置的值为什么要设置为1。

首先来回答第一个问题,auto_detect_line_endings配置在设置为1时,PHP将检查通过fgets()和file取得的数据中行结束符号是否符合Unix,MS-DOS,还是Macintosh的习惯。这使得PHP和Macintosh系统交互操作,但默认值是0,因为在检测第一行的EOL习惯时会有很小的性能损失。而且在Unix系统下使用回车符号作为项目分隔符的人们会遭遇向下不兼容的行为。

第二个问题,在读取env的时候为什么要设置为1,env作为一个配置性的文件,如果通过ftp等方式由一个Windows电脑上传到Linux服务器时,文件内容格式会因为兼容问题而使得env的内容无法正常读取为正确的配置数组,因此需要设置这个检测,将env的内容正确的读取。下面的图示为在Unix中读取env内容和在Windows中读取env内容的区别(编辑器可以设置文件分割方式,通过模拟的方式展示的):

第二步、将env数据放在系统变量中

在获取到env的数组之后,通过循环遍历数组进行setEnvironmentVariable()方法,进行系统变量的设置。在关键方法setEnvironmentVariable中,可以看到最终的设置名称和值通过apache_setenv和putenv原生方法分别对设置Apache子进程环境变量和系统环境变量。

public function load()
    {
        $this->ensureFileIsReadable();

        $filePath = $this->filePath;
        $lines = $this->readLinesFromFile($filePath);

        foreach ($lines as $line) {
            if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
                $this->setEnvironmentVariable($line);
            }
        }
        return $lines;
    }
public function setEnvironmentVariable($name, $value = null)
    {
        list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);

        $this->variableNames[] = $name;

        // Don't overwrite existing environment variables if we're immutable
        // Ruby's dotenv does this with `ENV[key] ||= value`.
        if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
            return;
        }

        // If PHP is running as an Apache module and an existing
        // Apache environment variable exists, overwrite it
        if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name) !== false) {
            apache_setenv($name, $value);
        }

        if (function_exists('putenv')) {
            putenv("$name=$value");
        }

        $_ENV[$name] = $value;
        $_SERVER[$name] = $value;
    }

一个比较有趣的事情是有时候我们的env在设置的时候存在特殊字符的时候会有不生效的情况,发生这种情况的原因大概率是和写入环境变量的时候的正则表达式有冲突,有些字符没有被匹配到导致的。核心方法如下:

protected function resolveNestedVariables($value)
    {
        if (strpos($value, '#39;) !== false) {
            $loader = $this;
            $value = preg_replace_callback(
                '/\${([a-zA-Z0-9_.]+)}/',
                function ($matchedPatterns) use ($loader) {
                    $nestedVariable = $loader->getEnvironmentVariable($matchedPatterns[1]);
                    if ($nestedVariable === null) {
                        return $matchedPatterns[0];
                    } else {
                        return $nestedVariable;
                    }
                },
                $value
            );
        }

        return $value;
    }

第三步、释放环境变量

在PHP官方手册中,对putenv方法的解释是这样的:添加 setting 到服务器环境变量。 环境变量仅存活于当前请求期间。 在请求结束时环境会恢复到初始状态。也就是当一次请求结束,那么这些设置的环境变量就会被释放。在本请求期间,可以通过getenv、apache_getenv、$_ENV等方式拿到env文件中配置的内容。在PHP源码中可以看到putenv最终的方法,确实是调用了efree来释放设置的变量。

#if defined(HAVE_PUTENV)
static void php_putenv_destructor(zval *zv) /* {{{ */
{
	putenv_entry *pe = Z_PTR_P(zv);

	if (pe->previous_value) {
# if defined(PHP_WIN32)
		/* MSVCRT has a bug in putenv() when setting a variable that
		 * is already set; if the SetEnvironmentVariable() API call
		 * fails, the Crt will double free() a string.
		 * We try to avoid this by setting our own value first */
		SetEnvironmentVariable(pe->key, "bugbug");
# endif
		putenv(pe->previous_value);
# if defined(PHP_WIN32)
		efree(pe->previous_value);
# endif
	} else {
# if HAVE_UNSETENV
		unsetenv(pe->key);
# elif defined(PHP_WIN32)
		SetEnvironmentVariable(pe->key, NULL);
# ifndef ZTS
		_putenv_s(pe->key, "");
# endif
# else
		char **env;

		for (env = environ; env != NULL && *env != NULL; env++) {
			if (!strncmp(*env, pe->key, pe->key_len) && (*env)[pe->key_len] == '=') {	/* found it */
				*env = "";
				break;
			}
		}
# endif
	}
#ifdef HAVE_TZSET
	/* don't forget to reset the various libc globals that
	 * we might have changed by an earlier call to tzset(). */
	if (!strncmp(pe->key, "TZ", pe->key_len)) {
		tzset();
	}
#endif

	efree(pe->putenv_string);
	efree(pe->key);
	efree(pe);
}
/* }}} */
#endif

相关推荐

RACI矩阵:项目管理中的角色与责任分配利器

作者:赵小燕RACI矩阵RACI矩阵是项目管理中的一种重要工具,旨在明确团队在各个任务中的角色和职责。通过将每个角色划分为负责人、最终责任人、咨询人和知情人四种类型,RACI矩阵确保每个人都清楚自己...

在弱矩阵组织中,如何做好项目管理工作?「慕哲制图」

慕哲出品必属精品系列在弱矩阵组织中,如何做好项目管理工作?【慕哲制图】-------------------------------慕哲制图系列0:一图掌握项目、项目集、项目组合、P2、商业分析和NP...

Scrum模式:每日站会(Daily Scrum)

定义每日站会(DailyScrum)是一个Scrum团队在进行Sprint期间的日常会议。这个会议的主要目的是为了应对Sprint计划中的不断变化,确保团队能够有效应对挑战并达成Sprint目标。为...

大家都在谈论的敏捷开发&Scrum,到底是什么?

敏捷开发作为一种开发模式,近年来深受研发团队欢迎,与瀑布式开发相比,敏捷开发更轻量,灵活性更高,在当下多变环境下,越来越多团队选择敏捷开发。什么是敏捷?敏捷是一种在不确定和变化的环境中,通过创造和响应...

敏捷与Scrum是什么?(scrum敏捷开发是什么)

敏捷是一种思维模式和哲学,它描述了敏捷宣言中的一系列原则。另一方面,Scrum是一个框架,规定了实现这种思维方式的角色,事件,工件和规则/指南。换句话说,敏捷是思维方式,Scrum是规定实施敏捷哲学的...

敏捷项目管理与敏捷:Scrum流程图一览

敏捷开发中的Scrum流程通常可以用一个简单的流程图来表示,以便更清晰地展示Scrum框架的各个阶段和活动。以下是一个常见的Scrum流程图示例:这个流程图涵盖了Scrum框架的主要阶段和活动,其中包...

一张图掌握项目生命周期模型及Scrum框架

Mockito 的最佳实践(mock方法)

记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到...

EffectiveJava-5-枚举和注解(java枚举的作用与好处)

用enum代替int常量1.int枚举:引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式。int枚举模式是类型不安全的,例如下面两组常量:性别和动物种...

Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!

Maven简介Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。Maven是一个跨平台的项目管理工具。主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。仔...

Java单元测试框架PowerMock学习(java单元测试是什么意思)

前言高德的技术大佬在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。”这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入实现...

Spring框架基础知识-第六节内容(Spring高级话题)

Spring高级话题SpringAware基本概念Spring的依赖注入的最大亮点是你所有的Bean对Spring容器的存在是没有意识的。但是在实际的项目中,你的Bean必须要意识到Spring容器...

Java单元测试浅析(JUnit+Mockito)

作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作...

揭秘Java代码背后的质检双侠:JUnit与Mockito!

你有没有发现,现在我们用的手机App、逛的网站,甚至各种智能设备,功能越来越复杂,但用起来却越来越顺畅,很少遇到那种崩溃、卡顿的闹心事儿?这背后可不是程序员一拍脑袋写完代码就完事儿了!他们需要一套严谨...

单元测试框架哪家强?Junit来帮忙!

大家好,在前面的文章中,给大家介绍了以注解和XML的方式分别实现IOC和依赖注入。并且我们定义了一个测试类,通过测试类来获取到了容器中的Bean,具体的测试类定义如下:@Testpublicvoid...

取消回复欢迎 发表评论: