在PHP for Beginners系列的后续文章中,今天的文章将重点介绍PHP如何搜索和连接文件。
为什么和为什么
PHP是一种脚本语言,最初是为快速雕刻主页而创建的(是的,是的,最初是个人个人年龄工具),后来开始在膝上创建商店,社交程序和其他工艺品,超出了预期的范围。 ,但是我为什么-编码的功能越多,对结构正确,消除代码重复,将其分成逻辑部分并仅在必要时进行连接的渴望就越大(这与您在你以前读过 位置,可以将其拆分成多个部分)。 为此,PHP具有多个功能,其一般含义是连接和解释指定的文件。 让我们看一个连接文件的例子:
如果运行
index.php脚本,则PHP将依次连接并执行所有这些操作:
$a = 0; $a++; $a++; echo $a;
连接文件时,其代码与连接所在行的作用域相同,因此此行中可用的所有变量将在包含的文件中可用。 如果在包含文件中声明了类或函数,则它们属于全局范围(除非为它们指定了名称空间)。
如果在函数内部连接文件,则包含的文件可以访问函数的作用域,因此以下代码也将起作用:
function() { $a = 0; include ('increment.php'); include ('increment.php'); echo $a; } a();
另外,我注意到魔术常量 : __DIR__
, __FILE__
__DIR__
, __FILE__
__DIR__
和其他常量 -它们与上下文相关,并在包含发生之前执行
连接文件的独特之处在于,在连接文件时,解析会切换到HTML模式,因此,所包含文件中的任何代码都必须用PHP标记括起来:
<?php
如果文件中仅包含PHP代码,则习惯上省略结束标记,以免意外地在结束标记之后忘记任何字符线程,这充满了问题(我将在下一篇文章中讨论)。
您是否看到包含10,000行的站点文件? 我的眼泪已经s(╥_╥)...
文件连接功能
如上所述,在PHP中,有几个用于连接文件的函数:
实际上,这些功能不完全是函数,它们是特殊的语言构造,可以省略括号。 除其他事项外,还有其他方法可以连接和执行文件,但您可以自己进行挖掘,让它成为您的“带有星号的任务”;)
让我们以
require
和
require_once
之间的差异为例,获取一个
echo.php文件:
<p>text of file echo.php</p>
我们将连接几次:
<?php
执行的结果将是与
echo.php文件的两个连接:
<p>text of file echo.php</p> <p>text of file echo.php</p>
还有其他一些会影响连接的指令,但您将不需要它们
-auto_prepend_file和
auto_append_file 。 这些伪指令允许您分别在连接所有文件之前和执行所有脚本之后安装将要连接的文件。 当可能需要时,我什至无法提出“实时”方案。
工作任务您可以使用
auto_prepend_file
和
auto_append_file
提出并实现脚本,只能在
php.ini ,
.htaccess或
httpd.conf中进行更改(请参见
PHP_INI_PERDIR ):)
在哪里看?
PHP在
include_path指令指定的目录中搜索包含文件。 此伪指令还会影响
fopen()
,
file()
,
readfile()
和
file_get_contents()
。 该算法非常简单-在搜索文件时,PHP会轮流检查
include_path
每个目录,直到找到要连接的文件为止;否则,它会返回错误。 要从脚本更改
include_path
,请使用
set_include_path()函数。
设置
include_path
时,需要考虑一件事-在Windows和Linux上将不同的字符用作路径分隔符-“;” 和“:”,因此在指定目录时,请使用
PATH_SEPARATOR
常量,例如:
在ini文件中编写
include_path
时,可以使用
${USER}
类的环境变量:
include_path = ".:${USER}/my-php-library"
如果在连接文件时包括绝对路径(以“ /”开头)或相对路径(以“。”或“ ..”开头),则
include_path
指令将被忽略,并且仅在指定路径上执行搜索。
也许值得一提谈论safe_mode ,但这已经是一个故事了(从5.4版开始),我希望您不会遇到它,但是如果突然发现它是什么,但是它过去了...
使用退货
我将告诉您一个小小的生活-如果包含的文件使用
return
构造返回了某些内容,则可以获取并使用此数据,因此您可以轻松地组织配置文件的连接,下面给出一个示例说明:
return [ 'host' => 'localhost', 'user' => 'root', 'pass' => '' ];
$dbConfig = require 'config/db.php'; var_dump($dbConfig);
有趣的事实,没有它也很好:如果在包含的文件中定义了函数,则可以在主文件中使用它们,而不管它们是在返回之前还是之后声明的
工作任务编写将从多个文件夹和文件收集配置的代码。 文件结构如下:
config |-- default | |-- db.php | |-- debug.php | |-- language.php | `-- template.php |-- development | `-- db.php `-- production |-- db.php `-- language.php
在这种情况下,代码应按以下方式工作:
- 如果系统环境中有一个
PROJECT_PHP_SERVER
变量,并且它等于development
,则应连接默认文件夹中的所有文件,应将数据包含在$config
变量中,然后应将开发文件夹中的文件连接起来,并且接收到的数据应研磨存储在$config
的相应项目 - 如果
PROJECT_PHP_SERVER
是production
则类似的行为(自然仅适用于生产文件夹) - 如果没有变量,或者设置不正确,则仅连接默认文件夹中的文件
自动连接
带有文件附件的构造看起来非常庞大,并且也遵循它们的更新-另外一件礼物,请从示例
文章中查看有关异常的一段代码:
避免这种“幸福”的第一个尝试是
__autoload函数的出现。 更确切地说,它甚至不是特定的功能,您必须自己定义此功能,并需要用它来连接类名所需的文件。 唯一的规则是,
对于每个类,应使用该类的名称创建一个单独的文件 (即,
myClass应该位于
myClass.php文件中)。 这是实现此功能
__autoload()
的示例(摘自官方手册中的注释):
我们将连接的类:
连接此类的文件:
现在,关于此函数的问题-设想一种情况,您正在连接第三方代码,并且有人已经为您的代码注册了
__autoload()
函数,瞧!
Fatal error: Cannot redeclare __autoload()
为了避免这种情况,我们创建了一个函数,该函数允许您将任意函数或方法注册为类加载器
-spl_autoload_register 。 即 我们可以创建几个具有任意名称的函数来加载类,然后使用
spl_autoload_register
注册。 现在
index.php
将如下所示:
标题“您知道吗?”:第一个参数spl_autoload_register()
是可选的,如果不带此参数调用该函数,则将函数spl_autoload用作加载程序,将在include_path
文件夹以及扩展名为.php
和.inc
文件中进行搜索,但这可以使用spl_autoload_extensions函数扩展该列表
现在,每个开发人员都可以注册他的加载器,主要是类名不匹配,但是如果使用名称空间,这应该不是问题。
由于spl_autoload_register()
这样的高级功能已经存在很长时间了,因此spl_autoload_register()
函数在PHP 7.1中已经声明为已弃用 ,这意味着在可预见的将来,此函数将被完全删除(X_x)
好吧,或多或少地清除了画面,尽管嘿,所有注册的引导加载程序在注册时都会分别排队,如果有人欺骗了他进入他的引导加载程序,则会出现一个非常令人讨厌的错误,而不是预期的结果。 为避免这种情况,成人聪明人介绍了一种标准,该标准使您可以毫无问题地连接第三方库,主要是它们中的类组织符合
PSR-0标准(已经有10年的历史了)或
PSR-4 。 标准中描述的要求的实质是什么:
- 每个库必须位于其自己的名称空间(所谓的供应商名称空间)中
- 每个名称空间必须具有自己的文件夹。
- 在命名空间内可能有子空间-也在单独的文件夹中
- 一类-一文件
- 扩展名为
.php
的文件名必须与类名完全匹配
手册中的示例:
全班名 | 命名空间 | 基本目录 | 全程 |
---|
\ Acme \日志\ Writer \ File_Writer | Acme \ Log \ Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
\光环\网络\响应\状态 | 光环\ Web | /路径/到/ aura-web / src / | /path/to/aura-web/src/Response/Status.php |
\ Symfony \核心\请求 | Symfony \核心 | ./供应商/ Symfony / Core / | ./vendor/Symfony/Core/Request.php |
\ Zend \ Acl | 曾德 | / usr / includes / Zend / | /usr/includes/Zend/Acl.php |
这两个标准之间的区别在于PSR-0支持没有命名空间的旧代码(即版本5.3.0之前的版本),PSR-4不受这种过时的困扰,甚至避免了不必要的文件夹嵌套。
由于这些标准,有可能出现诸如
作曲家之类的工具
-PHP的通用软件包管理器。 如果有人错过了,那么
pronskiy会提供有关此工具的好报告。
Php注射
我还想谈一谈在一个
index.php
为该站点创建一个单一入口点并将其称为MVC框架的每个人的第一个错误:
<?php $page = $_GET['page'] ?? die('Wrong filename'); if (!is_file($page)) { die('Wrong filename'); } include $page;
您看一下代码,只想在其中传输恶意代码:
// http://domain.com/index.php?page=../index.php // http://domain.com/index.php?page=config.ini // http://domain.com/index.php?page=/etc/passwd // , http://domain.com/index.php?page=user/backdoor.php
首先想到的是强制添加
.php
扩展名,但在某些情况下,可以“感谢”
零字节漏洞 (请阅读此漏洞
已修复了
很长时间 ,但是突然之间您遇到了比PHP 5.3更早的解释器,一般开发也建议):
// http://domain.com/index.php?page=/etc/passwd%00
在现代版本的PHP中,连接文件路径中零字节字符的存在会立即导致相应的连接错误,即使指定的文件存在并且可以连接,结果也始终是错误的,它按以下方式检查strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)
(来自PHP本身)
第二个“有价值”的想法是检查当前目录中的文件:
<?php $page = $_GET['page'] ?? die('Wrong filename'); if (strpos(realpath($page), __DIR__) !== 0) { die('Wrong path to file'); } include $page . '.php';
该检查的第三个但不是最后一个修改是使用
open_basedir指令,借助它的帮助,您可以指定确切的PHP将在其中搜索要连接的文件的目录:
<?php $page = $_GET['page'] ?? die('Wrong filename'); ini_set('open_basedir', __DIR__); include $page . '.php';
请注意,此指令不仅会影响文件的连接,还会影响文件系统的所有工作,即 包括此限制在内,您必须确保没有忘记指定目录之外的任何内容,缓存数据或任何用户文件(尽管功能is_uploaded_file()
和move_uploaded_file()
继续与用于下载文件的临时文件夹一起使用)。
还有哪些其他检查? 有很多选择,这完全取决于您的应用程序的体系结构。
我还想回想一下存在一个“很棒的”
allow_url_include指令(它取决于
allow_url_fopen ),它允许您连接和执行远程PHP文件,这对于您的服务器而言更加危险:
看到,记住并且从不使用,默认情况下关闭了好处。 您将需要比以前更少的功能;在所有其他情况下,请奠定正确的应用程序体系结构,使应用程序的各个部分通过API进行通信。
工作任务编写一个脚本,使您可以按名称连接当前文件夹中的php脚本,同时记住可能存在的漏洞并避免遗漏。
总结
本文是PHP的基础,因此请仔细研究,完成任务并且不要归档,没有人会教您。
聚苯乙烯
这是一系列文章“面向初学者的PHP”的转贴:
如果您对文章的材料或形式有任何评论,请在评论中描述要点,我们将使材料变得更好。