Michał Wachowski programista php & webdeveloper

Blog

Niedoróbki przestrzeni nazw, a może jednak nie?

Tak na prawdę, to ten wpis pierwotnie miał być odpowiedzią na blogu Tomasza Kowalczyka, ale rozrósł się na tyle, by stać się samodzielnym wpisem.

O co tu właściwie chodzi

Jest to odniesienie do tegoż wpisu Niedoróbki przestrzeni nazw PHP.

Nie da się importować całych przestrzeni

Importowanie jest, koślawe i brzydkie - ale jest. Nie da się niestety przejść do unqualified names. Tak na prawdę, to jest to coś w rodzaju "automatycznego aliasu": use \yada\foo; == use \yada\foo as foo;

namespace yada\foo;
class FooA { }
class FooB { }

namespace yada\bar;
class BarA { }
class BarB { }

namespace yada\buzz;
class BuzzA { }
class BuzzB { }

namespace flop;
use \yada\foo;
use \yada\bar;
use \yada\buzz as bz;

$obj = new \ArrayObject(array , 2);
$obj[] = new foo\FooA ;
$obj[] = new foo\FooB ;

$obj[] = new \yada\bar\BarA ;
$obj[] = new \yada\bar\BarB ;

$obj[] = new bz\BuzzA ;
$obj[] = new bz\BuzzB ;

Trzeba w kółko powtarzać instrukcje use, zapomniane importy/aliasy

I dobrze, dzięki temu każdy plik ma wpisane zależności. Jeszcze tylko tego było trzeba, by jakieś patałachy include'owały definicje "juzów" i potem gubiły pliki. Ctrl+C Ctrl+V jest tego warte.

Kolejnym "za" jest fakt, że wszystkie zależności są wypisane na początku. Jeśli dało by się zrobić import wszystkiego z danej przestrzeni - trzeba by zgadywać, co gdzie było. Przykładowo

Przykład:
namespace flop;
use foo/*;
use bar/*;

class Yada extends Hop { }

Pytanie brzmi: czy Hop jest w foo czy w bar?

Aliasy nie działają z funkcjami operującymi na klasach

Przykro mi, ale problem nie tkwi w przestrzeniach nazw, ale w błędnym wywołaniu Either a string containing the name of the class to reflect, or an object. Tak samo jest z innymi funkcjami/obiektami (przyznaję, że nie sprawdzałem wszystkich), a z tego co wiem, to string ma się nijak do przestrzeni nazw. Za to obiekt - ma pełną świadomość jakiej klasy jest instancją.

$Ref = new \ReflectionClass(stdClass);

To też się rypnie - wywali warrning, działanie jest tylko efektem "ratowania sytuacji" przez PHP.

Przestrzenie nazw mogą powodować kolizje nazw

Mogą i powinny! Powód to wyżej wymieniony "automatycznego aliasu"

Zagnieżdżanie przestrzeni nazw jest niedorobione

Jak na mój łeb, przyniosło by to więcej strat niż pożytku.

Przykład:
namespace foo;
class Foo	{ }

namespace foo/bar;
class Bar	extends Foo { }

W efekcie, PHP musiałby sprawdzić czy istnieje definicja Foo w foo/bar, potem w foo/ a potem w / - a żeby tego dokonać, musiał by mieć świadomość definicji klas w tychże przestrzeniach. Po cholerę includować pliki, z których się nie korzysta? Bo przecież zdefiniowanie przestrzeni foo/bar, oznaczałoby, że istnieje foo/ i trzeba sprawdzić co tam jest.

Pomijam tutaj problem z konfliktami - Foo może być implementacją w foo/ ale też może być abstrakcją w / i zgaduj co się wgra.

Jeżeli z nich korzystamy, to przecież trzeba mieć je w jakiś sposób zaincludowane.

Więc może traktujmy wszystkie "use" jako deklaracje "to będzie tutaj używane", samo wczytanie definicji klasy i tak odbywa się dopiero przy pierwszym tworzeniu instancji. Dzięki temu, na początku pliku mam pięknie napisane w jakiej przestrzeni działamy i jakie są zależności z zewnętrznymi elementami.

Wyszukiwanie nazw jest dziwaczne i niespójne

Tu się w pełni zgadzam z Pornelem.

Nazwy z przedrostkiem nie działają w deklaracjach klas

Ok, tu się zgodzę - nie działają. Może to i dobrze, bo brzydkie są.

Popularne projekty w PHP nadużywają podprzestrzeni nazw

Frameworki nadużywają wielu rzeczy. Ale ok, trzeba przyznać rację - tworzenie przestrzeni nazw dla jednego, jedynego wyjątku. Cóż tak to jest jak się przekłada teorię na rzeczywistość.

Przestrzenie nie mogą być ładowane automatycznie

Ano nie są bo i jak? Problem wg mnie tkwi w określaniu gdzie stała jest definiowana - jak określić miejsce jej definiowania?

Komentarze

  1. Tomasz Kowalczyk Tomasz Kowalczyk

    To się nazywa kontynuacja dyskusji - oby więcej takich odpowiedzi! :)
    PS. Proszę, zwiększ trochę czcionkę w tym formularzu - ledwo widzę, co piszę. :)

    Odpowiedz
  2. wachowski.michal@ wachowski.michal@

    Była 11px, za małe - jest 12. Większych nie dam (przynajmniej do momentu aż nie zmienię silnika...)

    Odpowiedz
  3. porneL porneL

    1. To, że use jest aliasem to wiem, ale nie zmienia to faktu, że trzeba use klepać w każdym pliku.
    2. Prawdziwe zależności klas robi się przez Dependency Injection.
    Ręczne deklaracje mogą kłamać - np. usuniesz zależność z kodu, ale zapomnisz usunąć use. W drugą stronę też może kłamać jak użyjesz fully-qualified name.
    Jak na listę zależności to takie bylejakie przybliżenie, które musisz utrzymywać, czy potrzebujesz, czy nie.
    A skąd bierze się klasa Hop to język D ma odpowiedź - jak jest kolizja, to wymaga aliasu, żeby nie było wątpliwości.
    Wiem, że import * z dowolnej cudzej przestrzeni jest ryzykowny, ale import * z mojego projektu nie ma ryzyka - ja wiem, co definiuję.
    3. object działa tylko z instancjami. Jak nie masz instancji, to nie ma wyboru - tylko string. W doccomment (adnotacje phpunit) - też tylko string.
    4. chyba nie zrozumiałeś. Tu chodzi o skopany tokenizer/gramatykę. W normalnej implementacji nie powinno być takiej sytuacji.
    5 & ostatnie — to, że nie ma łatwego oczywist

    Odpowiedz
  4. wachowski.michal@ wachowski.michal@

    ad. 2. Wg mnie, use'y można traktować jako listę. Przybliżoną? Zależy od tego jak piszesz. Piszesz źle to i dependency injection nie uratuje sytuacji.
    Import "z gwiazdką" był by fajny - nie przeczę. Jednak będzie to furtka dla bałaganu - ty importujesz, ja importuję, on importuje - wszyscy będą tak importować.
    ad 3. To czemu wpisujesz tam namespace i piszesz, że to "niedoróbka" ?
    ad 4. Zrozumiałem, po prostu chcesz by implementacja była jak najbardziej podobna do innych.
    ad 5. Sam stosuję mapy klas w swoim frameworku i wątpię by rozwiązało to problem. Moim zdaniem będzie to zbędnym narzutem.

    Odpowiedz

Dodaj komentarz