Php не видит класс из другого файла

Обновлено: 08.07.2024

Ранее я публиковал несколько вопросов относительно использования пространств имен в PHP, и, судя по тому, что я получил, приведенный ниже пример кода должен работать.

Однако я получаю ошибки, когда пытаюсь использовать пространство имен в PHP следующим образом. Вот первая ошибка при запуске приведенного ниже кода как есть .

E: \ Controller \ testing.php файл

E: \ Controller \ testcontroller.php Файл

E: \ Library \ Registry.class.php файл

Как видите, я попытался сделать это как можно проще, чтобы заставить работать часть пространства имен. Я пробовал разные варианты и, кажется, не могу понять.

Чтобы лучше понять, я опишу вам, как это работает. В вашем случае, когда вы выполняете use \Controller , вам становится доступно все пространство имен Controller , но не классы, которые находятся в этом пространстве имен. Так, например:

Если вы хотите импортировать именно Controller класс , вам нужно сделать use Controller\Controller - тогда этот класс будет доступен в вашей текущей области.

Как ни странно, я обнаружил, что в моем примере кода из вопроса выше, если я изменю все Namespace's , которые определены, на что-то вроде MyLibrary , это будет похоже на этот код ниже .

Файл E: \ Library \ Registry.class.php

Затем, когда я использую use MyLibrary\Registry; в другом файле, я могу получить к нему доступ, как я планировал .

Для меня это очень странно по той причине, что теперь имя класса тоже выглядит как Namespace . Поэтому мне не нужно было бы устанавливать пространство имен в «MyLibrary \ Library» для доступа к Registry , вместо этого я бы сделал это, как показано в этом ответе, чтобы иметь возможность получить к нему доступ, просто вызывая имя класса.

Я надеюсь, что это имеет смысл и поможет кому-то другому. Я не приму это как ответ, так как надеюсь, что кто-то с большим ноу-хау придет и опубликует лучший ответ с объяснением.

Когда вы помещаете класс Controller в пространство имен Controller , вы должны ссылаться на него таким образом:

\Controller будет классом в глобальном (по умолчанию) пространстве имен, т.е. как если бы вы вообще не использовали пространство имен.

Не очень хорошая идея называть пространство имен, как класс, потому что это сбивает с толку (и я думаю, что это то, что здесь происходит). В этот момент вы определяете псевдоним через use Controller эти ссылки либо на класс \Controller , либо на пространство имен \Controller , но ваш класс, поскольку он находится в пространстве имен, называется \Controller\Controller 1

Идея состоит в том, что в тот момент, когда вы пытаетесь получить доступ к классу с его относительным именем, он пытается сопоставить «первую часть» с любым псевдонимом, определенным с помощью use (помните, что use MyClass совпадает с use MyClass as MyClass . То, что стоит после as - это псевдоним).

Как вы можете видеть, PHP находит SomeComponent как первую часть и сопоставляет ее с псевдонимом SomeComponent в строке выше.

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

1 Он называется «Полное имя класса», если вы называете класс его полным именем.

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

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

Как вы можете видеть, я попытался сделать это как можно проще, чтобы получить часть пространства имен. Я пробовал разные варианты и не могу понять, как это понять.

Чтобы понять это лучше, я расскажу вам, как это работает. В вашем случае, когда вы выполняете use \Controller , пространство имен Controller становится доступным для вас, но не классы, которые находятся в этом пространстве имен. Итак, например:

Если вы хотите импортировать именно Controller класс, вам нужно сделать use Controller\Controller - тогда этот класс будет доступен в вашей текущей области.

Не так уж и хорошая идея назвать пространство имен, как класс, потому что это запутывает (и я думаю, что это то, что здесь происходит). В тот момент, когда вы определяете псевдоним через use Controller , это относится либо к классу \Controller , либо к пространству имен \Controller , но ваш класс, поскольку он находится в пространстве имен, называется \Controller\Controller 1

Идея заключается в том, что в тот момент, когда вы пытаетесь получить доступ к классу с его относительным именем, он пытается сопоставить "первую часть" с любым псевдонимом, определенным с помощью use (remeber use MyClass совпадает с use MyClass as MyClass . Вещь после as является псевдонимом).

Как вы можете видеть, PHP находит SomeComponent как первую часть и сопоставляет ее с SomeComponent -alias строкой выше.

Подробнее об этом можно прочитать в об пространствах имен.

1 Его называют "Полноценное имя класса", если вы назвали класс с его полным именем.

Когда вы помещаете класс Controller в пространство имен Controller , вы должны ссылаться на него следующим образом:

\Controller будет классом в глобальном (по умолчанию) пространстве имен, т.е. как будто вы вообще не использовали пространство имен.

Странно я обнаружил, что в моем примере кода из Вопроса выше, если я изменяю все Namespace's , которые определены как-то вроде MyLibrary , так что это будет как этот код ниже.

E:\Library\Registry.class.php Файл

Затем, когда я использую use MyLibrary\Registry; в другом файле, я могу получить к нему доступ, как я планировал.

Причина, по которой это очень странно для меня, заключается в том, что теперь имя класса выглядит как Namespace . Поэтому мне не нужно было устанавливать пространство имен в "MyLibrary\Library" для доступа к Registry , вместо этого я бы сделал это, как показано в этом ответе, чтобы иметь доступ к нему, просто называя имя класса.

Я надеюсь, что это имеет смысл и помогает кому-то другому. Я не буду принимать это как ответ, поскольку я надеюсь, что кто-то с большим количеством ноу-хау войдет и опубликует лучший ответ с объяснением.


Приветствую читателей блога! У вас наверняка уже есть небольшой опыт в кодинге на PHP. Возможно, вы уже разобрались с использованием переменных и базовых конструкций этого языка, а значит, логика вашего приложения увеличивается, как и количества кода.

Конечно, пока вся программа уменьшается в десяток строк, разбивать её на части не нужно. Но вы уже понимаете, что так будет не всегда: авторизация, отправка писем, взаимодействия с базой данных и т.д. – все это приведет к увеличению кода приложения.

Вы спросите: «Ну и что с того? Разве плохо писать всю логику в одном файле?». Стопроцентного ответа на этот вопрос нет, но мой опыт говорит, что код приложения, написанный в одном файле:

  • при дополнении и внесении новой логики приводит к частым ошибкам
  • требует большего времени для изучения другим разработчиком
  • через месяц-два при необходимости маленькой правки потребует у тебя гораздо больше времени на понимание кода, чем на саму правку.

Если этих доводов недостаточно – советую почитать книгу Роберта Мартина «Чистый код». А пока продолжу.

Представим, что у нас есть 2 файла: `index.php` и `1.php`, лежащих в одной директории.


Задача: вывести содержимое файла "1.php" в контейнере `body`, при запуске файла "index.php". Решить её можно разными способами, и в этом посте мы рассмотрим некоторые из них. Подключение PHP возможно с помощью разных инструкций:

  • `include`
  • `include_once`
  • `require`
  • `require_once`

Самый простой пример решения с `include`:

Результат запуска в браузере:


Как подключить PHP из другой директории

Теперь изменим условия. Переместим файл `1.php` в папку с названием `test`, которую создадим в директории с файлом `index.php`.


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

Далее изменим код в `index.php`.

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

Между папками, файлами и другими папками в пути устанавливаются разделители. Универсальный разделитель для различных операционных систем – `/`.

Если в папке `test` у нас была бы еще папка `lot`, в которой лежал файл `1.php`, то относительный путь выглядел бы так: 'test/lot/1.php'.

С путями немного разобрались – возвращаемся к инструкциям. Произведем изменения в файлах. Файл "index.php":

Посмотрим на изменение в выводе:


Как работает подключение кода PHP

Интерпретатор php «читает» код сверху вниз и слева направо, как мы читаем книги на русском языке. На исполнение от сервера ему указывается файл "index.php", а значит, чтение начинается с него. Дойдя до строчки с `include 'test/1.php'`, интерпретатор пытается найти и исполнить это файл так, как будто он является частью "index.php".

Перед подключением и исполнением файла "1.php" уже существует переменная `$say`, в которой содержится 'Hello world!'. При выполнении файла "1.php", содержимое этой переменной выводится на экран и создается переменная `$test`, которая в свою очередь и выводится на экран в файле `index.php`.

Если описанное выше непонятно, советую немного поиграться с файлами `1.php` и `index.php` создавая и выводя в них переменные.

Различия `include`, `include_once`, `require`, `require_once`

Переименуем файл "1.php"в файл "2.php" и обратимся к "index.php":


В итоге получаем ошибку. Но обратите внимание на то, что после вывода ошибки код PHP все равно продолжил выполнение и вывел `End`. Заменим `include` на `require` и запустим на выполнение.


В итоге видим похожие ошибки, но не видим вывода `End` в конце: после ошибки код php прекратил свою работу.

Разница между `include` и `require` заключается в том, что при подключении файла PHP первая инструкция позволяет продолжить выполнения скрипта, если не найден файл, а `require` завершает его, выводя фатальную ошибку.

Теперь рассмотрим отличие инструкций `require` и `require_once`. Внесем небольшие правки в наши файлы. Вот новый "index.php":


Как видно на скриншоте, с помощью `require` мы успешно подключили файл несколько раз. Снова внесем изменение в файлы. Новый файл "index.php":

И новый файл "2.php" — на этот раз объявим там функцию:


Второе подключение файла "2.php" приводит к ошибке, как раз потому что в этом файле происходит объявление функции. А в PHP-скрипте двух одинаковых функций быть не должно.

Теперь заменим все `require` на `require_once` и запустим снова:


Ура, работает! Но обратим внимание на то, что файл подключился только один раз.

Теперь вновь переименуем файл `2.php` в `1.php` и запустим "index.php".


`Require_once`, так же как и `require` завершает выполнение скрипта, если не найден файл указанный для подключения. Заменим `require_once` на `include_once`:


Ошибок стало больше, но код по-прежнему отработал до конца: end в конце картинки это подтверждает. Внесем правки в "index.php":


Подведём итоги

Чтобы подключить PHP-файлы, можно воспользоваться четырьмя похожими инструкциями — `include` и `include_once`, `require` и `require_once`.

  • Разница между `include` и `require`: при отсутствии файла последняя выводит фатальную ошибку, а первая — нет.
  • Разница между `include` и `include_once` (а также `require` и `require_once` соответственно): инструкции с “once” проверяют перед подключением, был ли этот файл подключен ранее. Если он подключался, повторного подключения не произойдет.
  • Разница между `require_once` и `include_once`: думаю, она понятна из двух предыдущих пунктов :)

Если вы хотите освоить PHP во всей его полноте — приглашаем вас на курсы PHP-разработки в GeekBrains. За шесть месяцев вы изучите не только работу с PHP, но и другие важные в профессии технологии — фреймворк Laravel, базы данных MS SQL и Postgre SQL, основы HTML/CSS и ООП. А также сможете пройти полноценную онлайн-стажировку!


Для чего и почему

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

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

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

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

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

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

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

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

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

Задание

Где ищет?

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

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

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

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

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

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

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

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

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

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

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

Поскольку уже давно существует такой продвинутый функционал как spl_autoload_register() , то функцию __autoload() хотят заявить как deprecated в PHP 7.1, а это значит, что в 7.2 её и вовсе может не быть

Пример из мануала:

Полное имя класса Пространство имён Базовая директория Полный путь
\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 поддерживает старый код без пространства имён, а PSR-4 избавлен от этого анахронизма, да ещё и позволяет избежать ненужной вложенности папок.

PHP-инъекция

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

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

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

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

Задание

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

В заключение

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