Contrar titlului, nu vreau să vorbesc despre valori europene. Nu, e vorba că, tot lucrând cu țârâita la Wp Trip Summary, am început să schimb treptat inclusiv convenția folosită pentru denumirea și organizarea claselor: din stilul PEAR A_B_C în structura bazată pe namespace-uri A\B\C.

Foto: Bogdan Stoica
Cum a le schimba pe toate deodată este un efort prea mare și-n general fastidios, cum pentru o singură clasă vor coexista o vreme ambele modalități de invocare, trebuie ca procesul să nu impieteze asupra funcționării; deci să pot folosi și A_B_C, și A\B\C. Ca un exemplu concret: și Abp01_Env, și WpTripSummary\Env.
Clasele vor fi redefinite, firește, folosind namespace-uri:
<?php
declare(strict_types=1);
namespace WpTripSummary {
if (!defined('ABP01_LOADED')) {
exit;
}
use mysqli_driver;
use MysqliDb;
use stdClass;
class Env {
//...
}
}
Pentru a putea fi recunoscute la rulare și după vechiul nume, se poate defini un alias cu class_alias(): o clasă existentă este făcută cunoscută printr-un alt nume, ceea ce pentru mine e perfect, că nu trebuie să întrețin sau să spurc locul c-o întreagă armată de fațade. Țin minte că multă vreme m-am tot mirat la ce-o fi bună și cred că e prima oară când o folosesc altcumva decât jucându-mă:
function abp01_init_legacy_class_aliases(): void {
abp01_legacy_class_alias(\WpTripSummary\Env::class,
'Abp01_Env');
abp01_legacy_class_alias(\WpTripSummary\Exception::class,
'Abp01_Exception');
}
function abp01_legacy_class_alias (string $newClass,string $legacyClass): void {
if (!class_exists($legacyClass, false)) {
class_alias($newClass, $legacyClass, true);
}
}
Păstrând al treilea parametru la true, PHP se va ocupa automat de încărcarea automată a clasei reale. Totul e grupat într-o singură funcție și-mi va fi ușor să le mătrășesc odată ce voi termina procesul, destul de rapid zic eu, maxim un an.
Mai rămâne totuși o problemă: analizorul din Intelephense (VS Code) nu știe să urmărească class_alias() și mă va bodogăni în consecință conform prevederilor în vigoare. Ca soluție, am un singur fișier fantomă – neinclus nicăieri, deci nu rulează, doar există – unde clasele reorganizate sunt denumire pe rit vechi și moștenesc din noua lor versiune:
<?php
/**
* This file is only used to satisfy intelephense
* and/or other static check support tools
*
* It is not included in the plug-in runtime,
* nor should it ever be.
*/
if (!defined('ABP01_LOADED')) {
exit;
}
/**
* @deprecated Use \WpTripSummary\Env instead.
*/
class Abp01_Env extends \WpTripSummary\Env {}
/**
* @deprecated Use \WpTripSummary\Exception instead.
*/
class Abp01_Exception extends \WpTripSummary\Exception {}
Fișierul nu reproduce perfect semantica unui alias – pentru analizor, clasa veche moștenește din cea nouă – însă, fiindcă nu participă niciodată la execuție, diferența rămâne în limitele prudenței. Ar putea conta pentru clase final, anumite utilizări self / static sau alte delicatese pe care analizorul încearcă să le deducă.
În schimb, mai există un punct gingaș: la construcția dinamică a modulelor individuale am un soi de injectare a dependințelor mai pentru săraci. Simplă, eficientă și de criză, bazată pe reflecție. Reflecția-mi va raporta denumirile vechi, ce nu vor fi găsite inițial în dicționar, deci obiectul unde-s folosite nu va putea fi construit. Ca să șuntez problema, am definit pur și simplu de două ori modalitatea de obținere a instanței dorite:
private function _getDefaultInjectableServiceFactories(): array {
return array(
Env::class
=> fn() => $this->getEnv(),
Abp01_Env::class
=> fn() => $this->getEnv()
);
}
Cu toate că nu poate obține premiul I cu coroniță pentru eleganță, e concentrat într-un singur loc și, odată șters fișierul fantomă, analizorul static va sublinia drept erori locurile cu denumiri caduce. Așadar, cum se spune în cele mai înalte cercuri academice: l-am făcut pă fraer!