Вложенные пространства имен только "отображают" файловую структуру?

Рейтинг: 0Ответов: 2Опубликовано: 21.02.2023

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

Например

<?php
namespace MyPHPProject;
class Strings
{}


<?php
namespace MyPHPProject\classes; 
// Файл Strings.php лежит в папке MyPHPProject\classes\
// Согласно стандарту, мы задаем пространство имен вида имя проекта\имя папки
class Strings
{}


<?php
namespace Abracadabra\Abra\cadabra\NoFolder\AnyText; 
// Файл Strings.php лежит в папке MyPHPProject\classes\
// Но мы решили просто написать какое то пространство имен-текст который первым пришел в голову
class Strings
{}

То есть вложенность namespace MyPHPProject и namespace MyPHPProject\classes никак не влияет на вызов этих классов.

Вложенность это только способ показать (спроецировать) файловую систему проекта - подсказать программисту где лежат эти файлы? Или вложенность как то ещё влияет на вызов классов?

Если неймспейс - это просто способ уникально идентифицировать класс, тогда не понятно зачем нужны относительные и абсолютные пути?

<?php

include_once __DIR__.'/Strings.php';

use MyPHPProject\Strings;

$strings=new Strings();
//$strings=new MyPHPProject\Strings(); // Ещё один вариант использования
//$strings=new \MyPHPProject\Strings(); // Ещё один вариант использования PHPStorm подсказывает, что \ не нужно

В PHP абсолютный путь создается при помощи DIR т.к. относительный путь, при вызове скрипта разным способом, рассчитывается относительно разных "объектов" - скрипт прекрасно работающий в браузере не найдет\подключит скрипты при вызове из cron.

А тут это зачем? Зачем пишут MyPHPProject\Strings и \MyPHPProject\Strings?

Ответы

▲ 2Принят

пространство имен это фактически уникальный идентификатор класса

Вполне можно рассматривать это с такой позиции. Например в том же Windows, если в папке d:\Downloads\ лежит файл Приколы.doc, то система считает, что его имя на самом деле d:\\Downloads\\Приколы.doc. Таким образом система считает файлы с одинаковым названием и расширением, но лежащие в разных папках - разными файлами.

namespace Abracadabra\Abra\cadabra\NoFolder\AnyText; // Файл Strings.php лежит в папке MyPHPProject\classes
// Но мы решили просто написать какое то пространство имен-текст который первым пришел в голову

Да, такое может быть, используя загрузчики, типа composer. Там можно задать папки или файлы немного кастомные, так скажем, у которых пространство имён никак не совпадает с физическим расположением в системе. Однако стОит понимать, что общая практика склоняется всё же к совпадению путей в пространстве имён и пути в проекте, именно для того, чтобы подсказать программисту где лежат эти файлы. Поэтому часто загрузчик делают по PSR-4 и в таком случае если указать выдуманный из головы namespace - то получишь ошибку Class 'app\models\test\Testy1' not found

вложенность как то ещё влияет на вызов классов?

Вложенность может влиять некоторым образом на производительность. Но не значительно. Как правило, при использовании автозагрузчика эти файлы могут быть перечислены у него в специальном файле, типа такого

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
    'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
    'Codeception\\Exception\\ExternalUrlException' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.php',
    'Codeception\\Lib\\Connector\\Yii2' => $vendorDir . '/codeception/module-yii2/src/Codeception/Lib/Connector/Yii2.php',
    'Codeception\\Lib\\Connector\\Yii2\\ConnectionWatcher' => $vendorDir . '/codeception/module-yii2/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php',
    'Codeception\\Lib\\Connector\\Yii2\\FixturesStore' => $vendorDir . '/codeception/module-yii2/src/Codeception/Lib/Connector/Yii2/FixturesStore.php',
);

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

зачем нужны относительные и абсолютные пути?

Относительный путь удобно использовать во вложенности. Если есть класс по пути app\models\test\Test и app\models\test\Test2, то в первом достаточно будет написать use Test2, а не use app\models\test\Test2.

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

namespace My\Other\NS {
    use \My\Other as Other; 
    use Other\NS as Duh; // Будет искать \My\Other\NS
}

namespace My\Other\NS {
    use My\Other as Other; 
    use Other\NS as Duh; // Будет искать \My\Other\NS\My\Other\NS
}

Такое может возникнуть например, когда есть "несколько корней". Например у используемого фреймворка, который лежит в папке vendor, может всё начинаться с Yii или Sympony, а в приложении всё начинается с app. В таком случае может произойти следующее:

namespace app\controllers;

class SiteController extends Controller {
    public function actionTest() {
        echo Yii::$app->user->isGuest;
    }
}

В этом случае будет ошибка Class 'app\controllers\Yii' not found потому что путь относительный. Однако если поставить путь "от корня", абсолютный, вот так:

echo \Yii::$app->user->isGuest;

то всё будет работать.

А тут это зачем? Зачем пишут MyPHPProject\Strings и \MyPHPProject\Strings?

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

Обратную черту перед классом любят ставить по многим причинам (мне лично кажется это избыточным), например для встроенных функций PHP, типа new \PDO или \strlen(...). Чтобы отличить свои написанные классы, от встроенных.

Ну и если есть конфликт имён (например при наследовании класса из другого неймспейса.), тоже можно это использовать.

namespace foo;

class Bar extends \baz\Bar { }

Однако я лично предпочитаю задать алиас и писать так:

namespace foo;
use baz\Bar as CoreBar;

class Bar extends CoreBar { }
▲ 1

1. Зачем нужно пространство имен - чтобы сгруппировать функционал и избежать конфликта имен, с помощью данного подхода реализуется один из основных принципов ООП, полиморфизм.

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

В действительности связано, т.к. с помощью него же как-то происходит автозагрузка классов.

а. Через composer, оный будет использовать стандарт PSR-4, в котором есть жеткий регламент привязки к директориям, если это проигнорировать, получите ошибку, т.к. по PSR-4, namespace = структуре директорий, но только от коря проекта.

б. Реализовать автозагрузку классов самому, тут как уже все поняли, можно накостылить хоть массив [ключь => значение], перебирать его и использовать в своем проекте таким образом любые пространства имен, которые уже никак не будут биться в структурой директорий.

2. Абсолютный путь - это хардкод относительно корня вашей файловой структуры на физической машине, вида: /home/you/php/index.php или же относительно текущей директории __DIR__./php/index.php и существует, чтобы сказать допустим вашему серверу, куда смотреть, в какой директории искать скрипты. В действительности, вариаций таких путей множество, напирмер, очень распространен в фреймворках от коря проекта, т.е. путь до коря проекта находится динамически, а по проекту хардкод: PathHelper::getProjectDir./public/index.php.

Но представьте, если бы вы в каждом своем скрипте аналогично захардкодили пути допустим к файлам/картинкам, а после чего еще и решили перенести свой проект в другую директорию, будете переписывать все пути? Вот для второго кейса в пределах проекта нужно использовать относительные пути, т.к. структура директорий в пределах проекта у вас будет одна и та же, даже после переноса, в таком случае, смело подключаете файлы и ссылаетесь на них относительно текущего скрипта.