Php подключить файл use

Обновлено: 06.07.2024

Вы наверняка сталкивались с этим ключевым словом use в php бесчисленное количество раз, в разных ситуациях. Разве это не забавно, что одно и то же ключевое слово используется в разных контекстах по очень разным причинам?

Давайте рассмотрим все места, где это ключевое слово ´use´ используется.

Импортирование классов с пространством имен

В настоящее время все современные PHP-фреймворки приняли соглашение о пространстве имен, что означает, что типичный контроллер будет выглядеть так:

Если вы не импортируете класс Article из его полного пространства имен App\Article с ключевым словом use, PHP не поймет, что это такое Article.

Вставка Трэйтов:

Хотите использовать Traits, чтобы поделиться кодом между несколькими классами? Легко! Давайте посмотрим на этот очень простой пример:

Метод slugify определен в Sluggable Trait. Но поскольку мы импортировали эту черту внутри класса Article, теперь вы можете использовать этот метод, как если бы он был определен в самом классе Article.

Обратите внимание, что ключевое слово use используется внутри скобок класса. Это основное отличие от первого примера.

Вне класса = импортировать класс
Внутри класса = импортировать трейт

Наследование переменных в замыканиях

Я снова возьму пример с Laravel. Взглянем на этот код:

В этом методе поиска мы хотим вернуть все записи с определенным названием категории.

Но этот код работать не будет. Фактически, он взорвется и вернет фатальную ошибку, сообщающую, что запрос $ не определен. Ху, странно?

Что ж, на самом деле это вполне логично, потому что функция, которую мы передаем в whereHas(), является смыканием. И замыкание имеет свою собственную область видимости, что означает, что оно не может получить доступ к переменным, которые определены вне ее области.

Что касается классов с пространством имен и свойств, мы должны импортировать переменные в область замыкания, чтобы их можно было использовать. И здесь вам поможет ключевое слово use.

image

PHP это скриптовый язык, созданный изначально для быстрого ваяния домашних страничек (да, да изначально это же был Personal Home Page Tools), а в дальнейшем на нём уже стали создавать магазины, социалки и другие поделки на коленке которые выходят за рамки задуманного, но к чему это я – а к тому, что чем больше функционала закодировано, тем больше желание его правильно структурировать, избавиться от дублирования кода, разбить на логические кусочки и подключать лишь при необходимости (это тоже самое чувство, которое возникло у вас, когда вы читали это предложение, его можно было бы разбить на отдельные кусочки). Для этой цели в PHP есть несколько функции, общий смысл которых сводится к подключению и интерпретации указанного файла. Давайте рассмотрим на примере подключения файлов:


Если запустить скрипт index.php, то PHP всё это будет последовательно подключать и выполнять:


Когда файл подключается, то его код оказывается в той же области видимости, что и строка в которой его подключили, таким образом все переменные, доступные в данной строке будут доступны и в подключаемом файле. Если в подключаемом файле были объявлены классы или функции, то они попадают в глобальную область видимости (если конечно для них не был указан namespace).

Если вы подключаете файл внутри функции, то подключаемые файлы получают доступ к области видимости функции, таким образом следующий код тоже будет работать:

Отдельно отмечу магические константы: __DIR__ , __FILE__ , __LINE__ и прочие — они привязаны к контексту и выполняются до того, как происходит включение

Особенностью подключения файлов является тот момент, что при подключении файла парсинг переключается в режим HTML, по этой причине любой код внутри включаемого файла должен быть заключен в PHP теги:


Если у вас в файле только PHP код, то закрывающий тег принято опускать, дабы случайно не забыть какие-нить символы после закрывающего тега, что чревато проблемами (об этом я ещё расскажу в следующей статье).

А вы видели сайт-файл на 10 000 строк? Аж слёзы на глазах (╥_╥)…

Функции подключения файлов

Как уже было сказано выше, в PHP существует несколько функций для подключения файлов:

    — включает и выполняет указанный файл, если не находит — выдаёт предупреждение E_WARNING — аналогично функции выше, но включает файл единожды — включает и выполняет указанный файл, если не находит — выдаёт фатальную ошибку E_ERROR — аналогично функции выше, но включает файл единожды
В действительности, это не совсем функции, это специальные языковые конструкции, и можно круглые скобочки не использовать. Кроме всего прочего есть и другие способы подключения и выполнения файлов, но это уже сами копайте, пусть это будет для вас «задание со звёздочкой» ;)

Давайте разберём на примерах различия между require и require_once , возьмём один файл echo.php:


И будем его подключать несколько раз:


Результатом выполнения будет два подключения файла echo.php:


Существует ещё парочка директив, которые влияют на подключение, но они вам не потребуются — auto_prepend_file и auto_append_file. Эти директивы позволяют установить файлы которые будут подключены до подключения всех файлов и после выполнения всех скриптов соответственно. Я даже не могу придумать «живой» сценарий, когда это может потребоваться.

Где ищет?

PHP ищет подключаемые файлы в директориях прописанных в директиве include_path. Эта директива также влияет на работу функций fopen() , file() , readfile() и file_get_contents() . Алгоритм работы достаточно простой — при поиске файлов PHP по очереди проверяет каждую директорию из include_path , пока не найдет подключаемый файл, если не найдёт — вернёт ошибку. Для изменения include_path из скрипта следует использовать функцию set_include_path().

При настройке include_path следует учитывать один важный момент — в качестве разделителя путей в Windows и Linux используются различные символы — ";" и ":" соответственно, так что при указании своей директории используйте константу PATH_SEPARATOR , например:


Когда вы прописываете include_path в ini файле, то можете использовать переменные окружения типа $ :

Если при подключении файла вы прописываете абсолютный путь (начинающийся с "/") или относительный (начинающийся с "." или ".."), то директива include_path будет проигнорирована, а поиск будет осуществлён только по указанному пути.

Возможно стоило бы рассказать и про safe_mode, но это уже давно история (с версии 5.4), и я надеюсь вы сталкиваться с ним не будете, но если вдруг, то чтобы знали, что такое было, но прошло.

Использование return

Расскажу о небольшом life-hack'е — если подключаемый файл возвращает что-либо с использованием конструкции return , то эти данные можно получить и использовать, таким образом можно легко организовать подключение файлов конфигурации, приведу пример для наглядности:

Занимательные факты, без которых жилось и так хорошо: если во включаемом файле определены функции, то они могут быть использованы в основном файле вне зависимости от того, были ли они объявлены до return или после

Написать код, который будет собирать конфигурацию из нескольких папок и файлов. Структура файлов следующая:


При этом код должен работать следующим образом:

  • если в системном окружении есть переменная PROJECT_PHP_SERVER и она равна development , то должны быть подключены все файлы из папки default, данные занесены в перемененную $config , затем подключены файлы из папки development, а полученные данные должны перетереть соответствующие пункты сохраненные в $config
  • аналогичное поведение если PROJECT_PHP_SERVER равна production (естественно только для папки production)
  • если переменной нет, или она задана неверно, то подключаются только файлы из папки default

Автоматическое подключение

Конструкции с подключением файлов выглядят очень громоздко, так и ещё и следить за их обновлением — ещё тот подарочек, зацените кусочек кода из примера статьи про исключения:


Первой попыткой избежать подобного «счастья» было появление функции __autoload. Сказать точнее, это была даже не определенная функция, эту функцию вы должны были определить сами, и уже с её помощью нужно было подключать необходимые нам файлы по имени класса. Единственным правилом считалось, что для каждого класса должен быть создан отдельный файл по имени класса (т.е. myClass должен быть внутри файла myClass.php). Вот пример реализации такой функции __autoload() (взят из комментариев к официальному руководству):

Класс который будем подключать:


Файл, который подключает данный класс:


Теперь о проблемах с данной функцией — представьте ситуацию, что вы подключаете сторонний код, а там уже кто-то прописал функцию __autoload() для своего кода, и вуаля:


Чтобы такого не было, создали функцию, которая позволяет регистрировать произвольную функцию или метод в качестве загрузчика классов — spl_autoload_register. Т.е. мы можем создать несколько функций с произвольным именем для загрузки классов, и зарегистрировать их с помощью spl_autoload_register . Теперь index.php будет выглядеть следующим образом:

Рубрика «а вы знали?»: первый параметр spl_autoload_register() не является обязательным, и вызвав функцию без него, в качестве загрузчика будет использоваться функция spl_autoload, поиск будет осуществлён по папкам из include_path и файлам с расширением .php и .inc , но этот список можно расширить с помощью функции spl_autoload_extensions
Теперь каждый разработчик может регистрировать свой загрузчик, главное чтобы имена классов не совпадали, но это не должно стать проблемой, если вы используете пространства имён.
Поскольку уже давно существует такой продвинутый функционал как spl_autoload_register() , то функцию __autoload() уже заявлена как deprecated в PHP 7.1, а это значит, что в обозримом будущем данную функцию и вовсе уберут (Х_х)
  1. Каждая библиотека должна жить в собственном пространстве имён (т.н. vendor namespace)
  2. Для каждого пространства имён должна быть создана собственная папка
  3. Внутри пространства имён могут быть свои подпространства — тоже в отдельных папках
  4. Один класс — один файл
  5. Имя файла с расширением .php должно точно соответствовать имени класса
Полное имя класса Пространство имён Базовая директория Полный путь
\Acme\Log\Writer\File_Writer Acme\Log\Writer ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\Status Aura\Web /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php
\Symfony\Core\Request Symfony\Core ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php
\Zend\Acl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php

Различия этих двух стандартов, лишь в том, что PSR-0 поддерживает старый код без пространства имён (т.е. до версии 5.3.0), а PSR-4 избавлен от этого анахронизма, да ещё и позволяет избежать ненужной вложенности папок.

Благодаря этим стандартам, стало возможно появление такого инструмента как composer — универсального менеджера пакетов для PHP. Если кто пропустил, то есть хороший доклад от pronskiy про данный инструмент.

PHP-инъекция

Ещё хотел рассказать о первой ошибки всех, кто делает единую точку входа для сайта в одном index.php и называет это MVC-фреймворком:


Смотришь на код, и так и хочется чего-нить вредоносного туда передать:


Первое, что приходит на ум — принудительно добавлять расширение .php , но в ряде случаев это можно обойти «благодаря» уязвимости нулевого байта (почитайте, эту уязвимость уже давно исправили, но вдруг вам попадётся интерпретатор более древний, чем PHP 5.3, ну и для общего развития тоже рекомендую):

В современных версиях PHP наличие символа нулевого байта в пути подключаемого файла сразу приводит к соответствующей ошибке подключения, и даже если указанный файл существует и его можно подключить, то в результате всегда будет ошибка, проверяется это следующим образом strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename) (это из недров самого PHP)

Вторая «стоящая» мысль, это проверка на нахождение файла в текущей директории:


Третья, но не последняя модификация проверки, это использование директивы open_basedir, с её помощью можно указать директорию, где именно PHP будет искать файлы для подключения:

Будьте внимательны, данная директива влияет не только на подключение файлов, но и на всю работу с файловой системой, т.е. включая данное ограничение вы должны быть уверены, что ничего не забыли вне указанной директории, ни кешированные данные, ни какие-либо пользовательские файлы (хотя функции is_uploaded_file() и move_uploaded_file() продолжат работать с временной папкой для загруженных файлов).

Какие ещё возможны проверки? Уйма вариантов, всё зависит от архитектуры вашего приложения.

Хотел ещё вспомнить о существовании «чудесной» директивы allow_url_include (у неё зависимость от allow_url_fopen), она позволяет подключать и выполнять удаленный PHP файлы, что куда как опасней для вашего сервера:


Увидели, запомнили, и никогда не пользуйтесь, благо по умолчанию выключено. Данная возможность вам потребуется чуть реже, чем никогда, во всех остальных случаях закладывайте правильную архитектуру приложения, где различные части приложения общаются посредством API.

Написать скрипт, который позволит подключать php-скрипты из текущей папки по названию, при этом следуют помнить о возможных уязвимостях и не допустить промашек.

В заключение

Данная статья — основа-основ в PHP, так что изучайте внимательно, выполняйте задания и не филоньте, за вас никто учить не будет.

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

Возможность ссылаться на внешнее абсолютное имя по псевдониму или импортированию - это важная особенность пространств имён. Это похоже на возможность файловых систем unix создавать символические ссылки на файл или директорию.

PHP может создавать псевдонимы имени/импортировать константы, функции, классы, интерфейсы и пространства имён.

Создание псевдонима имени выполняется с помощью оператора use . Вот пример, показывающий 5 типов импорта:

<?php
namespace foo ;
use My \ Full \ Classname as Another ;

// это тоже самое, что и использование My\Full\NSname as NSname
use My \ Full \ NSname ;

// импортирование глобального класса
use ArrayObject ;

// импортирование функции
use function My \ Full \ functionName ;

// псевдоним функции
use function My \ Full \ functionName as func ;

// импортирование константы
use const My \ Full \ CONSTANT ;

$obj = new namespace\ Another ; // создаёт экземпляр класса foo\Another
$obj = new Another ; // создаёт объект класса My\Full\Classname
NSname \ subns \ func (); // вызывает функцию My\Full\NSname\subns\func
$a = new ArrayObject (array( 1 )); // создаёт объект класса ArrayObject
// без выражения "use ArrayObject" мы создадим объект класса foo\ArrayObject
func (); // вызывает функцию My\Full\functionName
echo CONSTANT ; // выводит содержимое константы My\Full\CONSTANT
?>

Обратите внимание, что для имён в пространстве имён (абсолютные имена, содержащие разделитель пространств имён, такие как Foo\Bar , в отличие от глобальных имён, которые его не содержат, такие как FooBar ) нет необходимости в начальном обратном слеше (\) и его присутствие там не рекомендуется, так как импортируемые имена должны быть абсолютными и не обрабатываются относительно текущего пространства имён.

PHP дополнительно поддерживает удобное сокращение для задания нескольких операторов use в одной и той же строке

<?php
use My \ Full \ Classname as Another , My \ Full \ NSname ;

$obj = new Another ; // создаёт объект класса My\Full\Classname
NSname \ subns \ func (); // вызывает функцию My\Full\NSname\subns\func
?>

Импорт выполняется во время компиляции и поэтому не влияет на имена динамических классов, функций или констант.

<?php
use My \ Full \ Classname as Another , My \ Full \ NSname ;

$obj = new Another ; // создаёт объект класса My\Full\Classname
$a = 'Another' ;
$obj = new $a ; // создаёт объект класса Another
?>

В дополнение, импорт распространяется только на неполные и полные имена. Абсолютные имена не затрагиваются операцией импорта.

<?php
use My \ Full \ Classname as Another , My \ Full \ NSname ;

$obj = new Another ; // создаёт объект класса My\Full\Classname
$obj = new \ Another ; // создаёт объект класса Another
$obj = new Another \ thing ; // создаёт объект класса My\Full\Classname\thing
$obj = new \ Another \ thing ; // создаёт объект класса Another\thing
?>

Обзор правил для импорта

Ключевое слово use должно быть указано в самом начале файла (в глобальной области) или внутри объявления пространства имён. Это необходимо потому, что импорт выполняется во время компиляции, а не во время исполнения, поэтому оно не может быть заключено в блок. Следующий пример показывает недопустимое применение ключевого слова use :

<?php
namespace Languages ;

function toGreenlandic ()
use Languages \ Danish ;

Замечание:

Правила импорта задаются на каждый файл отдельно. Это означает, что присоединяемые файлы НЕ будут наследовать правила импорта из родительского файла.

Описание группирования в одном операторе use

Классы, функции и константы, импортируемые из одного и того же namespace , могут группироваться в одном операторе use .

use some \namespace\ ClassA ;
use some \namespace\ ClassB ;
use some \namespace\ ClassC as C ;

use function some \namespace\ fn_a ;
use function some \namespace\ fn_b ;
use function some \namespace\ fn_c ;

use const some \namespace\ ConstA ;
use const some \namespace\ ConstB ;
use const some \namespace\ ConstC ;

// Эквивалентно следующему групповому использованию
use some \namespace\< ClassA , ClassB , ClassC as C >;
use function some \namespace\< fn_a , fn_b , fn_c >;
use const some \namespace\< ConstA , ConstB , ConstC >;

в этой index.php файл я хочу создать экземпляр класса Resp .

я знаю, что могу использовать require или include ключевые слова для включения файла с классом:

но я хочу импортировать классы без использования require или include . Я пытаюсь понять, как use ключевое слово работает. Я пробовал эти шаги, но ничего не работает:

как ключевое слово use работы? Могу ли я использовать его для импорта классов?

use не включает в себя ничего. Он просто импортирует указанное пространство имен (или класс) в текущую область

Если вы хотите, чтобы классы были загружены - читайте о Автозагрузка

нет, вы не можете импортировать класс с use ключевое слово. Вы должны использовать include / require заявление. Даже если вы используете PHP auto loader, все равно autoloader придется использовать либо include или require внутренне.

цель использовать ключевые слова:

рассмотрим случай, когда у вас есть два класса с одинаковым именем; вы найдете это странным, но когда вы работаете с большой структурой MVC, это происходит. Так что если у вас есть два класса с одно и то же имя, поместите их в разные пространства имен. Теперь рассмотрим, когда ваш автоматический загрузчик загружает оба класса (делает по require ), и вы собираетесь использовать объект класса. В этом случае компилятор запутается, какой объект класса загружать среди двух. Чтобы помочь компилятору принять решение, вы можете использовать use заявление, чтобы он мог принять решение, какой из них будет использоваться.

в настоящее время основные механизмы использую include или require через composer и psr

1) композитор

2) PSR-4 автозагрузчик

просмотр их может помочь вам в дальнейшем. Вы также можете использовать псевдоним для обращения к точному классу. Предположим, у вас есть два класса с одинаковым именем, скажем Mailer С двумя различными пространствами имен:

и

и если вы хотите использовать оба класса почтовой программы одновременно, то вы можете использовать псевдоним.

далее в коде, если вы хотите получить доступ к этим объектам класса, то вы можете сделать следующее:

он будет ссылаться на исходный класс.

некоторые могут запутаться, что тогда нет подобных имен классов, то нет никакого использования use ключевое слово. Ну, вы можете использовать __autoload($class) функция, которая будет вызываться автоматически, когда use заявление выполняется с класса, который будет использоваться в качестве аргумента, и это может помочь вам загрузить класс во время выполнения "на лету" по мере необходимости.

обратитесь к этому ответ чтобы узнать больше о классе автозапуска.

вам все равно придется включить/потребовать класс, иначе PHP не будет знать о пространстве имен.
Вам не обязательно делать это в то же самое. Вы можете сделать это в файле начальной загрузки, например. (или использовать автозагрузчик, но это не тема на самом деле)

проблема, скорее всего, вам нужно будет использовать автоматический загрузчик, который возьмет имя класса (в этом случае разбить на"\") и сопоставит его со структурой каталогов.

вы можете проверить эту статью о Автозагрузка функциональность PHP. В рамках уже существует множество реализаций этого типа функциональности.

Я на самом деле реализован раньше. Вот это ссылке.

Я согласен с зеленым, Symfony нужно пространство имен, так почему бы не использовать их ?

вот как начинается пример класса контроллера:

пространство имен Acme\DemoBundle\Controller;

использовать Symfony\Bundle\FrameworkBundle\Controller\Controller;

класс WelcomeController расширяет контроллер

на use ключевое слово для псевдонимов в PHP и оно не импортирует классы. Это действительно помогает
1) Когда у вас есть классы с одинаковыми именами в разных пространствах имен
2) Избегайте использования очень длинного имени класса снова и снова.

могу ли я использовать его для импорта классов?

вы не можете сделать это так, кроме примеров выше. Вы также можете использовать ключевое слово use внутри классов для импорта черт, например:

Читайте также: