Learn/flight_vs_laravel

Flight pret Laravel

Kas ir Laravel?

Laravel ir pilnvērtīga ietvara sistēma ar visiem zvaniņiem un svilpēm un pārsteidzošu izstrādātājiem veltītu ekosistēmu, bet ar izmaksām veiktspējā un sarežģītībā. Laravel mērķis ir nodrošināt izstrādātājam augstāko produktivitātes līmeni un padarīt izplatītās uzdevumus vieglus. Laravel ir lieliska izvēle izstrādātājiem, kuri vēlas izveidot pilnvērtīgu, uzņēmējdarbības līmeņa tīmekļa lietojumprogrammu. Tas nāk ar dažām kompromisiem, īpaši veiktspējas un sarežģītības ziņā. Laravel pamatu apguve var būt viegla, bet prasmju iegūšana ietvarā var prasīt zināmu laiku.

Ir arī tik daudz Laravel moduļu, ka izstrādātāji bieži jūtas, ka vienīgais veids, kā atrisināt problēmas, ir caur šiem moduļiem, kad patiesībā varētu izmantot citu bibliotēku vai uzrakstīt savu kodu.

Priekšrocības salīdzinājumā ar Flight

Trūkumi salīdzinājumā ar Flight

Learn/migrating_to_v3

Migrēšana uz v3

Atpakaļsaderība lielākoties ir saglabāta, bet ir daži izmaiņas, par kurām jums jāzina, migrējot no v2 uz v3. Ir daži izmaiņas, kas pārāk daudz nonāca pretrunā ar dizaina modeļiem, tāpēc bija jāveic dažas korekcijas.

Izvades buferizācijas uzvedība

v3.5.0

Izvades buferizācija ir process, kurā PHP skripta ģenerētais izvade tiek saglabāts buferī (iekšēji PHP), pirms tas tiek nosūtīts klientam. Tas ļauj modificēt izvadi pirms tās nosūtīšanas klientam.

MVC lietojumprogrammā Kontrolētājs ir "vadītājs" un tas pārvalda, ko dara skats. Ja izvade tiek ģenerēta ārpus kontrolētāja (vai Flight gadījumā dažreiz anonīma funkcija), tas pārkāpj MVC modeli. Šī izmaiņa ir lai būtu vairāk saskaņota ar MVC modeli un lai padarītu ietvaru paredzamāku un vieglāk lietojamu.

v2 versijā izvades buferizācija tika apstrādāta tā, ka tā konsekventi neaizvēra savu izvades buferi, kas apgrūtināja vienības testēšanu un straumēšanu. Lielākajai daļai lietotāju šī izmaiņa var pat nebūt ietekme uz jums. Tomēr, ja jūs izvadat saturu ārpus izsaucamajām funkcijām un kontrolētājiem (piemēram, āķī), visticamāk, saskarsieties ar problēmām. Izvades satura āķos un pirms ietvara faktiskās izpildes varēja darboties iepriekš, bet tas nedarbosies turpmāk.

Kur var rasties problēmas

// index.php
require 'vendor/autoload.php';

// tikai piemērs
define('START_TIME', microtime(true));

function hello() {
    echo 'Hello World';
}

Flight::map('hello', 'hello');
Flight::after('hello', function(){
    // tas faktiski būs kārtībā
    echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});

Flight::before('start', function(){
    // tādas lietas izraisīs kļūdu
    echo '<html><head><title>My Page</title></head><body>';
});

Flight::route('/', function(){
    // tas faktiski ir kārtībā
    echo 'Hello World';

    // Arī šis vajadzētu būt kārtībā
    Flight::hello();
});

Flight::after('start', function(){
    // tas izraisīs kļūdu
    echo '<div>Your page loaded in '.(microtime(true) - START_TIME).' seconds</div></body></html>';
});

Ieslēgšana v2 renderēšanas uzvedībai

Vai jūs joprojām varat saglabāt savu veco kodu tāpat, kā tas ir, bez pārstrādes, lai tas darbotos ar v3? Jā, varat! Jūs varat ieslēgt v2 renderēšanas uzvedību, iestatot konfigurācijas opciju flight.v2.output_buffering uz true. Tas ļaus jums turpināt izmantot veco renderēšanas uzvedību, bet ieteicams to labot turpmāk. v4 versijā ietvarā tas tiks noņemts.

// index.php
require 'vendor/autoload.php';

Flight::set('flight.v2.output_buffering', true);

Flight::before('start', function(){
    // Tagad tas būs kārtībā
    echo '<html><head><title>My Page</title></head><body>';
});

// vairāk koda 

Dispatcher izmaiņas

v3.7.0

Ja jūs tieši esat izsaukuši statiskas metodes Dispatcher klasei, piemēram, Dispatcher::invokeMethod(), Dispatcher::execute() utt., jums būs jāatjaunina jūsu kods, lai tieši neizsauktu šīs metodes. Dispatcher ir pārveidots, lai būtu vairāk objektorientēts, tāpēc atkarību injekcijas konteineri var tikt izmantoti vieglāk. Ja jums jāizsauc metode līdzīgi kā to darīja Dispatcher, jūs varat manuāli izmantot kaut ko līdzīgu $result = $class->$method(...$params); vai call_user_func_array().

halt() stop() redirect() un error() izmaiņas

v3.10.0

Noklusētā uzvedība pirms 3.10.0 bija notīrīt gan galvenes, gan atbildes ķermeni. Tas tika mainīts, lai notīrītu tikai atbildes ķermeni. Ja jums jānotīra arī galvenes, jūs varat izmantot Flight::response()->clear().

Learn/configuration

Konfigurācija

Pārskats

Flight nodrošina vienkāršu veidu, kā konfigurēt dažādus sistēmas aspektus atbilstoši jūsu lietojumprogrammas vajadzībām. Daži iestatījumi ir noteikti pēc noklusējuma, bet tos var pārrakstīt pēc nepieciešamības. Varat arī iestatīt savus mainīgos, ko izmantot visā lietojumprogrammā.

Skaidra, slāņveida konfigurācija (failu noklusējumi + vides noslēpumi) arī palīdz AI kodēšanas rīkiem: aģenti mācās vienu vietu literāļiem un vienu vietu noslēpumiem, nevis izdomā $_ENV lasījumus kontrolieros.

Izpratne

Jūs varat pielāgot noteiktu Flight darbību, iestatot konfigurācijas vērtības ar set metodi.

Flight::set('flight.log_errors', true);

Strukturētā lietojumprogrammā (ieskaitot skeleton) jūs parasti ielādējat projekta iestatījumus no app/config/config.php un pēc tam lietojat atbilstošās atslēgas uz Engine (piemēram, flight.base_url, flight.views.path). Varat arī ievadīt nelielu konfigurācijas objektu kontrolieros, nevis lasīt globālos mainīgos visur — tas ir draudzīgāk testiem un aģentiem, kas seko AGENTS.md.

Pamata lietošana

Flight konfigurācijas opcijas

Šeit ir saraksts ar visiem pieejamajiem konfigurācijas iestatījumiem:

Ielādētāja konfigurācija

Ir vēl viens konfigurācijas iestatījums ielādētājam. Tas ļauj automātiski ielādēt klases ar _ nosaukumā.

// Iespējot klašu ielādi ar apakšsvītrām
// Noklusējums ir true
Loader::$v2ClassLoading = false;

Atcerieties, ka automātiskā ielāde ir atkarīga arī no mapju reģistra atbilstības jūsu nosaukumvietām — īpaši ar skeleton App\ + app/Controller/ izkārtojumu.

Projekta konfigurācija un .env (skeleton modelis)

Flight kodolam nav nepieciešami .env faili. Daudzas lietojumprogrammas izmanto tikai PHP konfigurācijas masīvu. Oficiālais skeleton noslāņo konfigurāciju, lai noslēpumi paliktu ārpus git, kamēr Runway joprojām var droši pārrakstīt literālo konfigurāciju:

  1. .env / reālā vide — noslēpumi un izvietošanas pārrakstīšanas (giģnorēti).
  2. app/config/config.php — literālo PHP masīvu noklusējumi (kopēti no config_sample.php). Dodiet priekšroku bez $_ENV[...] izteiksmēm šajā failā: rīki, piemēram, runway config:set, var to pārrakstīt kā statiskas vērtības un iecept noslēpumus failā.
  3. Sapludināšana startēšanas laikā — vide uzvar kartētajām atslēgām; lietojumprogrammas kods lasa konfigurācijas objektu vai $app->get(), nevis $_ENV kontrolieros.

Piemērs config_sample.php / config.php formas (vienkāršots):

<?php
// Tikai literāļi — noslēpumi pieder .env skeleton darbplūsmā
return [
    'app' => [
        'env' => 'development',
        'debug' => true,
        'base_url' => '/',
        'timezone' => 'UTC',
    ],
    'database' => [
        'driver' => 'sqlite', // vai mysql, vai '' lai atspējotu
        'host' => 'localhost',
        'dbname' => '',
        'user' => '',
        'password' => '',
        'file_path' => __DIR__ . '/../../database.sqlite',
    ],
    // ...
];
# .env.example → .env (skeleton)
APP_ENV=development
APP_DEBUG=true
FLIGHT_BASE_URL=/
DB_DRIVER=sqlite
# DB_PASSWORD=...

Šī sadalīšana ir apzināta AI draudzīgiem projektiem: instrukcijas var teikt "noklusējumi config.php, noslēpumi .env, ievadiet Config / Engine — nekad neizgudrojiet env piekļuvi kontrolierī." Esošās lietojumprogrammas var pilnībā ignorēt .env un saglabāt vienu konfigurācijas failu.

Mainīgie

Flight ļauj saglabāt mainīgos, lai tos varētu izmantot jebkurā jūsu lietojumprogrammas vietā.

// Saglabājiet savu mainīgo
Flight::set('id', 123);

// Citur jūsu lietojumprogrammā
$id = Flight::get('id');

Lai pārbaudītu, vai mainīgais ir iestatīts, varat:

if (Flight::has('id')) {
  // Izdariet kaut ko
}

Varat notīrīt mainīgo, rīkojoties šādi:

// Notīra id mainīgo
Flight::clear('id');

// Notīra visus mainīgos
Flight::clear();

Piezīme: Tas, ka varat iestatīt mainīgo, nenozīmē, ka jums tas būtu jādara. Lietojiet šo funkciju taupīgi. Iemesls ir tāds, ka viss, kas šeit glabājas, kļūst par globālo mainīgo. Globālie mainīgie ir slikti, jo tos var mainīt no jebkuras vietas jūsu lietojumprogrammā, apgrūtinot kļūdu izsekošanu. Turklāt tas var sarežģīt tādas lietas kā vienību testēšana. Dodiet priekšroku konstruktora ievadīšanai (kā skeleton + Dice iestatījumā) pakalpojumiem un konfigurācijai, kas nepieciešama kontrolieriem.

Kļūdas un izņēmumi

Visas kļūdas un izņēmumi tiek uztverti ar Flight un nodoti error metodei, ja flight.handle_errors ir iestatīts uz true.

Noklusējuma uzvedība ir nosūtīt vispārīgu HTTP 500 Internal Server Error atbildi ar zināmu kļūdas informāciju.

Varat pārrakstīt šo uzvedību savām vajadzībām:

Flight::map('error', function (Throwable $error) {
  // Apstrādāt kļūdu
  echo $error->getTraceAsString();
});

Pēc noklusējuma kļūdas netiek reģistrētas tīmekļa serverī. To var iespējot, mainot konfigurāciju:

Flight::set('flight.log_errors', true);

404 Nav atrasts

Ja URL nevar atrast, Flight izsauc notFound metodi. Noklusējuma uzvedība ir nosūtīt HTTP 404 Not Found atbildi ar vienkāršu ziņojumu.

Varat pārrakstīt šo uzvedību savām vajadzībām:

Flight::map('notFound', function () {
  // Apstrādāt nav atrasts
});

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/ai

AI un izstrādātāju pieredze ar Flight

Pārskats

Flight ir veidots, lai strādātu ar AI kodēšanas rīkiem—nevis cīnītos pret tiem. Neliels, paredzams API, skaidrs lietotnes izkārtojums oficiālajā skeleton un projektam specifiski instrukciju faili nozīmē, ka asistenti, piemēram, GitHub Copilot, Cursor, Windsurf, Claude Code un Gemini, var sekot tiem pašiem modeļiem, ko jūs rakstītu paši.

Ar iebūvētām Runway komandām savienošanai ar LLM pakalpojumu sniedzējiem un projektu instrukciju ģenerēšanai, Flight palīdz jums un jūsu komandai saņemt konsekventu un atbilstošu palīdzību bez nepieciešamības ielīmēt vienu un to pašu kontekstu katrā tērzēšanā.

Izpratne

AI kodēšanas asistenti ir visnoderīgākie, kad tie saprot jūsu projekta kontekstu, konvencijas un mērķus. Flight AI palīgi ļauj jums:

Šie rīki ir iekļauti Flight pamata CLI (caur Runway) un ir iepriekš konfigurēti oficiālajā flightphp/skeleton sākuma projektā.

Ko skeleton nodrošina AI

Oficiālais sākuma projekts uzskata AGENTS.md par patiesības avotu AI rīkiem:

Fails Loma
AGENTS.md (projekta sakne) Globālie noteikumi, palaišanas plūsma, namespaces, DI, “ko nedrīkst darīt”
Scoped AGENTS.md zem app/, migrations/, tests/ utt. Vieglas, mapēm specifiskas padomes, kad strādājat šajā kokā
SECURITY.md Noslēpumi, galvenes, XSS/SQL, ziņošana—drošība paliek apzināta un atsevišķa

Skeleton nav atsevišķa stila faila Copilot / Cursor / Gemini / Windsurf. Norādiet savu asistentu uz saknes AGENTS.md (un ļaujiet tam sekot saitēm uz scoped failiem). Cilvēki var pilnībā ignorēt šos failus un izmantot README; izkārtojums abos gadījumos ir vienāds.

Dokumentācija māca API; skeleton māca izkārtojumu. Īsi Flight:: piemēri šajā dokumentācijā ir lieliski mācībām. Skeleton lietotnē dodiet priekšroku App\… klasēm, konstruktora injekcijai un $this->app nevis statiskajam fasādes stilam kontrolieros. Skatiet Instalācija un Autoloading.

Pamata lietošana

LLM akreditācijas datu iestatīšana

Komanda ai:init ved jūs cauri projekta savienošanai ar LLM pakalpojumu sniedzēju.

php runway ai:init

Jums tiks prasīts:

Tas izveido akreditācijas datus, kas tiek izmantoti vēlākiem LLM pieprasījumiem (piemēram, instrukciju ģenerēšanai).

Piemērs:

Laipni lūgti AI Init!
Kuru LLM API vēlaties izmantot? [1] openai, [2] grok, [3] claude: 1
Ievadiet bāzes URL LLM API [https://api.openai.com]:
Ievadiet savu API atslēgu priekš openai: sk-...
Ievadiet modeļa nosaukumu, kuru vēlaties izmantot (piem., gpt-4, claude-3-opus utt.) [gpt-4o]:
Akreditācijas dati saglabāti .runway-creds.json

Projektam specifisku AI instrukciju ģenerēšana

Komanda ai:generate-instructions izveido vai atjaunina instrukcijas AI kodēšanas asistentiem, pielāgotas jūsu projektam.

php runway ai:generate-instructions

Jums būs jāatbild uz dažiem jautājumiem (apraksts, datubāze, template engine, drošība, komandas lielums utt.). Flight izmanto jūsu LLM pakalpojumu sniedzēju, lai ģenerētu instrukcijas, un ieraksta tās galvenokārt:

Atkarībā no CLI versijas un opcijām komanda var ierakstīt arī rīkiem specifiskas kopijas vecākām darba plūsmām (piemēram, Copilot, Cursor, Windsurf vai Gemini noteikumu failus). Jauniem projektiem, kas veidoti no skeleton, uzskatiet AGENTS.md (plus jebkurus scoped AGENTS.md failus, ko saglabājat zem app/) par vienīgo patiesības avotu—neuzturiet piecus atšķirīgus instrukciju failus manuāli.

Piemērs:

Lūdzu, aprakstiet, kam jūsu projekts ir paredzēts? Mana lieliskā API
Kuru datubāzi plānojat izmantot? MySQL
Kuru HTML template engine plānojat izmantot (ja tāds ir)? twig
Vai drošība ir svarīgs šī projekta elements? (j/n) j
...
AI instrukcijas veiksmīgi atjauninātas.

Tagad AI rīki var ieteikt kodu, kas atbilst jūsu faktiskajai tehnoloģiju kopai un izkārtojumam—nevis vispārīgai PHP apmācībai.

Papildu lietošana

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/unit_testing_and_solid_principles

Šis raksts sākotnēji tika publicēts vietnē Airpair 2015. gadā. Visi nopelni tiek doti Airpair un Brianam Fentonam, kurš sākotnēji uzrakstīja šo rakstu, lai gan vietne vairs nav pieejama, un raksts pastāv tikai Wayback Machine. Šis raksts ir pievienots vietnei mācību un izglītības mērķiem PHP kopienai kopumā.

1 Iestatīšana un konfigurācija

1.1 Turiet to aktuālu

Sāksim ar to – pārsteidzoši maz PHP instalāciju praksē ir aktuālas vai tiek uzturētas aktuālas. Vai tas ir dēļ koplietotas hostinga ierobežojumiem, noklusējumiem, kurus neviens nemaina, vai laika/budžeta trūkuma atjaunināšanas testēšanai, PHP binārās faili mēdz tikt atstāti novārtā. Tāpēc viena skaidra labākā prakse, kurai vajadzētu pievērst vairāk uzmanības, ir vienmēr izmantot aktuālu PHP versiju (5.6.x šajā rakstā). Turklāt ir svarīgi plānot regulāras atjaunināšanas gan pašam PHP, gan jebkādiem paplašinājumiem vai piegādātāju bibliotēkām, kuras jūs izmantojat. Atjaunināšanas sniedz jaunas valodas funkcijas, uzlabotu ātrumu, mazāku atmiņas lietojumu un drošības atjauninājumus. Jo biežāk jūs atjauninat, jo mazāk sāpīgs process kļūst.

1.2 Iestatiet saprātīgus noklusējumus

PHP veic pienācīgu darbu, iestatot labus noklusējumus ar saviem php.ini.development un php.ini.production failiem, bet mēs varam darīt labāk. Piemēram, tie mums nekur nenorāda datumu/laiku joslu. Tas ir saprātīgi no izplatīšanas viedokļa, bet bez tās PHP izmetīs E_WARNING kļūdu katru reizi, kad izsaucam datuma/laika saistītu funkciju. Lūk, daži ieteikti iestatījumi:

1.3 Paplašinājumi

Tas arī ir laba ideja deaktivizēt (vai vismaz neaktivizēt) paplašinājumus, kurus jūs neizmantosiet, piemēram, datu bāzes draiverus. Lai redzētu, kas ir aktivizēts, izpildiet phpinfo() komandu vai dodieties uz komandrindu un izpildiet šo.

$ php -i

Informācija ir tā pati, bet phpinfo() pievieno HTML formatējumu. CLI versija ir vieglāk caurvadāma uz grep, lai atrastu specifisku informāciju. Piem.

$ php -i | grep error_log

Viens ierobežojums šai metodei: iespējams, ka dažādas PHP iestatījumi attiecas uz tīmekļa versiju un CLI versiju.

2 Izmantojiet Composer

Tas varētu būt pārsteigums, bet viena no labākajām praksēm mūsdienu PHP rakstīšanai ir rakstīt mazāk no tā. Lai gan ir taisnība, ka viens no labākajiem veidiem, kā kļūt labākam programmēšanā, ir darīt to, ir daudz problēmu, kas jau ir atrisinātas PHP telpā, piemēram, maršrutēšana, pamata ievades validācijas bibliotēkas, vienību konvertēšana, datu bāzes abstrakcijas slāņi utt... Vienkārši apmeklējiet Packagist un izpētiet. Jūs, visticamāk, atradīsiet, ka ievērojamas daļas no problēmas, kuru mēģināt atrisināt, jau ir uzrakstītas un testētas.

Kaut arī vilinoši ir rakstīt visu kodu pašam (un ar to nav nekā slikta – rakstīt savu ietvaru vai bibliotēku kā mācību pieredzi), jums vajadzētu cīnīties pret šīm "Neizgudrots Šeit" izjūtām un ietaupīt sev daudz laika un galvassāpju. Sekojiet PIE doktrīnai – Lepni Izgudrots Citur. Arī, ja jūs izvēlaties rakstīt savu kaut ko, neatklaidiet to, ja tas nedara kaut ko ievērojami atšķirīgu vai labāku nekā esošie piedāvājumi.

Composer ir pakotņu pārvaldnieks PHP, līdzīgs pip Python, gem Ruby un npm Node. Tas ļauj definēt JSON failu, kas uzskaita jūsu koda atkarības, un tas mēģinās atrisināt šīs prasības, lejupielādējot un instalējot nepieciešamo kodu saišķus.

2.1 Composer instalēšana

Mēs pieņemam, ka tas ir lokāls projekts, tāpēc instalēsim Composer instanci tikai šim projektam. Dodieties uz savu projektu direktoriju un izpildiet šo:

$ curl -sS https://getcomposer.org/installer | php

Atcerieties, ka caurvadīt jebkuru lejupielādi tieši uz skripta interpreters (sh, ruby, php utt...) ir drošības risks, tāpēc izlasiet instalēšanas kodu un pārliecinieties, ka esat ar to mierā, pirms izpildāt jebkuru šādu komandu.

Ērtības dēļ (ja jūs dodat priekšroku rakstīt composer install nevis php composer.phar install), jūs varat izmantot šo komandu, lai instalētu vienu Composer kopiju globāli:

$ mv composer.phar /usr/local/bin/composer
$ chmod +x composer

Jums var būt nepieciešams izpildīt šos ar sudo, atkarībā no jūsu failu atļaujām.

2.2 Composer izmantošana

Composer ir divas galvenās atkarību kategorijas, kuras tas var pārvaldīt: "require" un "require-dev". Atkarības, kas uzskaitītas kā "require", tiek instalētas visur, bet "require-dev" atkarības tiek instalētas tikai tad, kad tās tiek īpaši pieprasītas. Parasti tās ir rīki, kad kods ir aktīvā attīstībā, piemēram, PHP_CodeSniffer. Zemāk redzams piemērs, kā instalēt Guzzle, populāru HTTP bibliotēku.

$ php composer.phar require guzzle/guzzle

Lai instalētu rīku tikai attīstības mērķiem, pievienojiet karogu --dev:

$ php composer.phar require --dev 'sebastian/phpcpd'

Tas instalē PHP Copy-Paste Detector, citu koda kvalitātes rīku kā attīstības atkarību.

2.3 Install vs update

Kad mēs pirmo reizi izpildām composer install, tas instalēs jebkuras bibliotēkas un to atkarības, balstoties uz composer.json failu. Kad tas ir paveikts, composer izveido bloķēšanas failu, paredzami sauktu composer.lock. Šis fails satur sarakstu ar atkarībām, kuras composer atrada mums, un to precīzajām versijām ar hashiem. Tad jebkuru nākamo reizi, kad izpildām composer install, tas paskatīsies bloķēšanas failā un instalēs tās precīzās versijas.

composer update ir mazliet atšķirīga būtne. Tas ignorēs composer.lock failu (ja tāds ir) un mēģinās atrast visjaunākās katras atkarības versijas, kas joprojām apmierina ierobežojumus composer.json. Tad tas rakstīs jaunu composer.lock failu, kad tas ir pabeigts.

2.4 Autoloadēšana

Gan composer install, gan composer update ģenerēs autoloader mums, kas stāsta PHP, kur atrast visas nepieciešamās failus, lai izmantotu bibliotēkas, kuras mēs tikko instalējām. Lai to izmantotu, vienkārši pievienojiet šo rindiņu (parasti uz bootstrap failu, kas tiek izpildīts katru pieprasījumu):

require 'vendor/autoload.php';

3 Sekojiet labiem dizaina principiem

3.1 SOLID

SOLID ir mnemonika, lai atgādinātu mums par piecām galvenajām principiem labā objekt-orientētā programmatūras dizainā.

3.1.1 S - Single Responsibility Principle

Tas norāda, ka klasēm vajadzētu būt tikai vienai atbildībai, vai, citādi sakot, tām vajadzētu būt tikai viens iemesls izmaiņām. Tas labi iekļaujas Unix filozofijā ar daudzām mazām rīkām, kas dara vienu lietu labi. Klases, kas dara tikai vienu lietu, ir daudz vieglāk testēt un atkļūdot, un tās mazāk jūs pārsteigs. Jūs nevēlaties, lai metodes izsaukums uz Validator klasi atjaunina db ierakstus. Lūk, piemērs ar SRP pārkāpumu, kādu jūs bieži redzētu aplikācijā, kas balstīta uz ActiveRecord pattern.

class Person extends Model
{
    public $name;
    public $birthDate;
    protected $preferences;
    public function getPreferences() {}
    public function save() {}
}

Tā ir diezgan pamata entity modelis. Bet viena no šīm lietām šeit nepieder. Entitātes modelim vajadzētu būt tikai uzvedībai, kas saistīta ar entītāti, ko tā pārstāv, tam nevajadzētu būt atbildīgam par pašas saglabāšanu.

class Person extends Model
{
    public $name;
    public $birthDate;
    protected $preferences;
    public function getPreferences() {}
}
class DataStore
{
    public function save(Model $model) {}
}

Tas ir labāk. Person modelis atgriežas pie tā, ka dara tikai vienu lietu, un saglabāšanas uzvedība ir pārvietota uz noturības objektu. Ņemiet vērā, ka es tikai type hinted uz Model, ne Person. Mēs atgriezīsimies pie tā, kad nonāksim pie L un D SOLID daļām.

3.1.2 O - Open Closed Principle

Ir lielisks tests šim, kas diezgan labi apkopo, par ko šis princips ir: padomājiet par funkciju, ko ieviest, iespējams, pēdējo, pie kuras strādājāt vai strādājat. Vai jūs varat ieviest šo funkciju esošajā kodsbazē TIKAI pievienojot jaunas klases un nemainot nevienu esošu klasi sistēmā? Jūsu konfigurācija un vadošais kods saņem mazliet atlaides, bet lielākajā daļā sistēmu tas ir pārsteidzoši grūti. Jums ir jāpaļaujas daudz uz polimorfisku nosūtīšanu, un lielākā daļā kodsbazju tas vienkārši nav iestatīts. Ja jūs interesējaties par to, ir labs Google runas video YouTube par polimorfismu un kodu rakstīšanu bez If, kas izpēta to tālāk. Kā bonuss, runu vada Miško Hevery, kuru daudzi var zināt kā AngularJs izveidotāju.

3.1.3 L - Liskov Substitution Principle

Šis princips ir nosaukts Barbara Liskov vārdā, un tas ir izdrukāts zemāk:

"Objekti programmā vajadzētu būt aizvietojami ar viņu apakštipu instancēm, nekaitējot programmas pareizībai."

Tas viss izklausās labi, bet tas ir skaidrāk ilustrēts ar piemēru.

abstract class Shape
{
    public function getHeight();
    public function setHeight($height);
    public function getLength();
    public function setLength($length);
}

Šis pārstāvēs mūsu pamata četrām pusēm formu. Nekas izsmalcināts šeit.

class Square extends Shape
{
    protected $size;
    public function getHeight() {
        return $this->size;
    }
    public function setHeight($height) {
        $this->size = $height;
    }
    public function getLength() {
        return $this->size;
    }
    public function setLength($length) {
        $this->size = $length;
    }
}

Šī ir mūsu pirmā forma, Kvadrāts. Diezgan taisni uz priekšu forma, vai ne? Jūs varat pieņemt, ka ir konstruktors, kur mēs iestatām izmērus, bet no šīs realizācijas jūs redzat, ka garums un augstums vienmēr būs vienādi. Kvadrāti vienkārši ir tādi.

class Rectangle extends Shape
{
    protected $height;
    protected $length;
    public function getHeight() {
        return $this->height;
    }
    public function setHeight($height) {
        $this->height = $height;
    }
    public function getLength() {
        return $this->length;
    }
    public function setLength($length) {
        $this->length = $length;
    }
}

Tā ir cita forma. Joprojām ir tās pašas metodes paraksti, tā joprojām ir četrām pusēm forma, bet ko, ja mēs sākam mēģināt izmantot tās citu vietā? Tagad pēkšņi, ja mēs mainām formas augstumu, mēs vairs nevaram pieņemt, ka formas garums sakritīs. Mēs esam pārkāpuši līgumu, ko bijām noslēguši ar lietotāju, kad devām viņam mūsu Kvadrāta formu.

Tas ir tipisks LSP pārkāpuma piemērs, un mums ir vajadzīgs šāds princips, lai vislabāk izmantotu tipa sistēmu. Pat duck typing nepateiks mums, ja pamata uzvedība ir atšķirīga, un, tā kā mēs to nevaram zināt bez tam lūstot, ir labāk pārliecināties, ka tā nav atšķirīga.

3.1.3 I - Interface Segregation Principle

Šis princips prasa priekšroku daudzām mazām, smalkām saskarnēm pret vienu lielu. Saskarnes vajadzētu balstīties uz uzvedību, nevis "tas ir viena no šīm klasēm". Padomājiet par saskarnēm, kas nāk ar PHP. Traversable, Countable, Serializable, tādas lietas. Tās reklamē spējas, ko objekts piemīt, nevis ko tas manto. Tāpēc turiet savas saskarnes mazas. Jūs nevēlaties, lai saskarnei būtu 30 metodes, 3 ir daudz labāks mērķis.

3.1.4 D - Dependency Inversion Principle

Jūs, iespējams, esat dzirdējuši par to citās vietās, kur runāja par Dependency Injection, bet Dependency Inversion un Dependency Injection nav gluži viena un tā pati lieta. Dependency inversion patiesībā ir veids, kā teikt, ka jums vajadzētu paļauties uz abstrakcijām savā sistēmā, nevis uz tās detaļām. Ko tas nozīmē jums ikdienā?

Neizmantojiet mysqli_query() tieši visā savā kodā, izmantojiet kaut ko kā DataStore->query() tā vietā.

Šī principa kodols ir par abstrakcijām. Tas vairāk ir par teikumu "izmantojiet datu bāzes adapteri" nevis paļaujoties uz tiešiem izsaukumiem uz lietām kā mysqli_query. Ja jūs tieši izmantojat mysqli_query pusē no savām klasēm, tad jūs visu saistāt tieši ar savu datu bāzi. Nekas pret MySQL šeit, bet, ja jūs izmantojat mysqli_query, šāda veida zemlaukuma detaļas vajadzētu būt paslēptas tikai vienā vietā, un tad šī funkcionalitāte vajadzētu būt pieejama caur ģenerisku aploksni.

Tagad es zinu, ka tas ir mazliet izmantots piemērs, ja jūs par to domājat, jo reizes, kad jūs pilnībā mainīsiet savu datu bāzes dzinēju pēc produkta ieviešanas, ir ļoti, ļoti zemas. Es to izvēlējos, jo domāju, ka cilvēki būs pazīstami ar ideju no sava koda. Arī, pat ja jums ir datu bāze, ar kuru jūs zināt, ka paliksiet, šī abstraktā aploksnes objekts ļauj jums labot kļūdas, mainīt uzvedību vai ieviest funkcijas, kuras jūs vēlaties, lai jūsu izvēlētajai datu bāzei būtu. Tas arī padara unit testēšanu iespējamo, kur zemlaukuma izsaukumi to nedarītu.

4 Objektu vingrinājumi

Šis nav pilns izpēte šiem principiem, bet pirmie divi ir viegli atcerēties, sniedz labu vērtību un var tikt nekavējoties piemēroti gandrīz jebkurai kodsbazei.

4.1 Ne vairāk kā viens indentācijas līmenis uz metodi

Tas ir noderīgs veids, kā domāt par metožu sadalīšanu mazākos gabalos, atstājot kodu, kas ir skaidrāks un vairāk pašdokumentējošs. Jo vairāk indentācijas līmeņu jums ir, jo vairāk metode dara un jo vairāk stāvokļa jums jāseko prātā, kamēr jūs ar to strādājat.

Tūlīt es zinu, ka cilvēki iebildīs pret to, bet tas ir tikai vadlīnija/heiistika, ne cieta un ātra likums. Es negaidu, ka kāds izpildīs PHP_CodeSniffer noteikumus tam (lai gan cilvēki ir).

Apskatīsim ātru paraugu, kā tas varētu izskatīties:

public function transformToCsv($data)
{
    $csvLines = array();
    $csvLines[] = implode(',', array_keys($data[0]));
    foreach ($data as $row) {
        if (!$row) {
            continue;
        }
        $csvLines[] = implode(',', $row);
    }
    return $csvLines;
}

Kamēr šis nav briesmīgs kods (tas tehniski ir pareizs, testējams utt...), mēs varam darīt daudz vairāk, lai to padarītu skaidru. Kā mēs samazinātu ligzdas līmeņus šeit?

Mēs zinām, ka mums ir jāvienkāršo foreach cikla saturs (vai to noņemt pilnībā), tāpēc sāksim tur.

if (!$row) {
    continue;
}

Šī pirmā daļa ir viegla. Tas tikai ignorē tukšas rindas. Mēs varam saīsināt šo procesu, izmantojot iebūtu PHP funkciju pirms pat nokļūšanas ciklā.

$data = array_filter($data);
foreach ($data as $row) {
    $csvLines[] = implode(',', $row);
}

Tagad mums ir mūsu viens ligzdas līmenis. Bet, skatoties uz to, viss, ko mēs darām, ir funkcijas piemērošana katram vienumam masīvā. Mums pat nav vajadzīgs foreach cikls, lai to darītu.

$data = array_filter($data);
$csvLines = array_map(function($row) {
    return implode(',', $row);
}, $data);

Tagad mums vispār nav ligzdas, un kods, visticamāk, būs ātrāks, jo mēs darām visu ciklu ar iebūtiem C funkcijām nevis PHP. Mums ir jāiesaistās mazliet viltībā, lai nodotu komatu uz implode, tāpēc jūs varētu argumentēt, ka apstāšanās pie iepriekšējā soļa ir daudz saprotamāka.

4.2 Mēģiniet neizmantot else

Tas tiešām attiecas uz divām galvenajām idejām. Pirmā ir vairākas return paziņojumi no metodes. Ja jums ir pietiekami daudz informācijas, lai pieņemtu lēmumu par metodes rezultātu, ejiet uz priekšu un pieņemiet to un return. Otrā ir ideja, kas pazīstama kā Guard Clauses. Tās ir pamata validācijas pārbaudes, kas kombinētas ar agrīniem return, parasti metodes augšdaļā. Ļaujiet man parādīt, ko es domāju.

public function addThreeInts($first, $second, $third) {
    if (is_int($first)) {
        if (is_int($second)) {
            if (is_int($third)) {
                $sum = $first + $second + $third;
            } else {
                return null;
            }
        } else {
            return null;
        }
    } else {
        return null;
    }
    return $sum;
}

Tā ir diezgan taisna uz priekšu atkal, tā pievieno 3 intus kopā un atgriež rezultātu, vai null, ja kāds no parametriem nav integers. Ignorējot to, ka mēs varētu apvienot visas šīs pārbaudes vienā rindā ar AND operatoriem, es domāju, ka jūs redzat, cik ligzotais if/else struktūra padara kodu grūtāk sekot. Tagad paskatieties uz šo piemēru.

public function addThreeInts($first, $second, $third) {
    if (!is_int($first)) {
        return null;
    }
    if (!is_int($second)) {
        return null;
    }
    if (!is_int($third)) {
        return null;
    }
    return $first + $second + $third;
}

Man šis piemērs ir daudz vieglāk sekot. Šeit mēs izmantojam guard clauses, lai verificētu mūsu sākotnējos pieņēmumus par parametriem, ko mēs pārejam, un nekavējoties izkāpjam no metodes, ja tās neizdodas. Mēs arī vairs neesam ar starpposma mainīgo, lai izsekotu summu caur visu metodi. Šajā gadījumā mēs esam verificējuši, ka mēs jau esam uz laimīgā ceļa, un varam vienkārši darīt to, ko mēs atnācām darīt. Atkal mēs varētu vienkārši darīt visas šīs pārbaudes vienā if, bet princips vajadzētu būt skaidrs.

5 Unit testēšana

Unit testēšana ir prakse rakstīt mazus testus, kas verificē uzvedību jūsu kodā. Tie gandrīz vienmēr tiek rakstīti tajā pašā valodā kā kods (šajā gadījumā PHP) un ir paredzēti, lai būtu pietiekami ātri, lai tos izpildītu jebkurā laikā. Tie ir ārkārtīgi vērtīgi kā rīks, lai uzlabotu jūsu kodu. Papildus acīmredzamajām priekšrocībām, nodrošinot, ka jūsu kods dara to, ko jūs domājat, unit testēšana var sniegt ļoti noderīgu dizaina atsauksmi. Ja koda gabals ir grūti testējams, tas bieži parāda dizaina problēmas. Tie arī dod jums drošības tīklu pret regresijām, un tas ļauj jums refactorēt daudz biežāk un attīstīt savu kodu tīrākam dizainam.

5.1 Rīki

Ir vairāki unit testēšanas rīki PHP, bet tālu un prom visizplatītākais ir PHPUnit. Jūs varat instalēt to, lejupielādējot PHAR failu tieši, vai instalēt to ar composer. Tā kā mēs izmantojam composer visam pārējam, mēs parādīsim šo metodi. Arī, tā kā PHPUnit, visticamāk, netiks izvietots ražošanā, mēs varam instalēt to kā dev atkarību ar šo komandu:

composer require --dev phpunit/phpunit

5.2 Testi ir specifikācija

Vissvarīgākā unit testu loma jūsu kodā ir nodrošināt izpildāmu specifikāciju tam, ko kods ir paredzēts darīt. Pat ja testa kods ir nepareizs vai kodā ir kļūdas, zināšanas par to, ko sistēma ir paredzēts darīt, ir nenovērtējama.

5.3 Rakstiet savus testus vispirms

Ja jums ir bijusi iespēja redzēt testu kopu, kas rakstīta pirms koda, un vienu, kas rakstīta pēc koda pabeigšanas, tās ir satriecoši atšķirīgas. "Pēc" testi ir daudz vairāk uztraukti par klases īstenošanas detaļām un labas līnijas seguma nodrošināšanu, savukārt "pirms" testi vairāk ir par vēlamās ārējās uzvedības verificēšanu. Tas tiešām ir tas, par ko mums rūp ar unit testiem, ir pārliecināties, ka klase izrāda pareizo uzvedību. Uz īstenošanu fokusēti testi faktiski padara refactorēšanu grūtāku, jo tie lūzt, ja klases iekšējās daļas mainās, un jūs tikko esat pazaudējis OOP informācijas slēptuves priekšrocības.

5.4 Kas padara labu unit testu

Labiem unit testiem ir daudz no šīm īpašībām:

Ir iemesli iet pret dažiem no šiem, bet kā vispārēji vadlīnijas tās jums kalpos labi.

5.5 Kad testēšana ir sāpīga

Unit testēšana liek jums izjust sliktā dizaina sāpes uz priekšu – Michael Feathers

Kad jūs rakstāt unit testus, jūs piespiežat sevi faktiski izmantot klasi, lai paveiktu lietas. Ja jūs rakstāt testus beigās, vai vēl sliktāk, vienkārši metat kodu pāri sienai QA vai kam, lai rakstītu testus, jūs nesaņemat atsauksmi par to, kā klase faktiski uzvedas. Ja mēs rakstām testus un klase ir reāla sāpe izmantot, mēs to uzzināsim, kamēr mēs to rakstām, kas ir gandrīz lētākais laiks to labot.

Ja klase ir grūti testējama, tas ir dizaina trūkums. Dažādi trūkumi parādās dažādos veidos, kaut gan. Ja jums ir jādara daudz mocking, jūsu klasei, iespējams, ir pārāk daudz atkarību vai jūsu metodes dara pārāk daudz. Jo vairāk iestatīšanas jums ir jāveic katram testam, jo vairāk iespējams, ka jūsu metodes dara pārāk daudz. Ja jums ir jāraksta patiešām sarežģīti testa scenāriji, lai izmantotu uzvedību, klases metodes, iespējams, dara pārāk daudz. Ja jums ir jāizrok iekšā daudz privātu metožu un stāvokļa, lai testētu lietas, varbūt tur ir cita klase, kas mēģina izkļūt. Unit testēšana ir ļoti laba, lai atklātu "aisberga klases", kur 80% no tā, ko klase dara, ir paslēpts aiz protected vai private koda. Es kādreiz biju liels fans padarīt cik iespējams daudz protected, bet tagad es sapratu, ka es tikai liku savām individuālajām klasēm atbildēt par pārāk daudz, un īstā risinājums bija sadalīt klasi mazākos gabalos.

Rakstījis Brian Fenton - Brian Fenton ir bijis PHP izstrādātājs 8 gadus Vidējos Rietumos un Bay Area, šobrīd Thismoment. Viņš fokusējas uz kodu amatniecību un dizaina principiem. Blogs www.brianfenton.us, Twitter @brianfenton. Kad viņš nav aizņemts būdams tēvs, viņam patīk ēdiens, alus, spēles un mācīšanās.

Learn/security

Drošība

Pārskats

Drošība ir liela lieta, runājot par tīmekļa lietojumprogrammām. Jūs vēlaties pārliecināties, ka jūsu lietojumprogramma ir droša un ka jūsu lietotāju dati ir drošībā. Flight piedāvā vairākas funkcijas, lai palīdzētu jums aizsargāt savas tīmekļa lietojumprogrammas.

Oficiālais skelets ietver arī īpašu SECURITY.md un drošības galveņu starpprogrammatūru, lai AI kodēšanas rīkiem (un cilvēkiem) būtu viena apzināta vieta noslēpumiem, galvenēm un XSS/SQL noteikumiem — atsevišķi no vispārējā koda stila failā AGENTS.md.

Izpratne

Ir vairāki izplatīti drošības apdraudējumi, par kuriem jums vajadzētu zināt, veidojot tīmekļa lietojumprogrammas. Daži no visizplatītākajiem apdraudējumiem ir:

Veidnes palīdz pret XSS, pēc noklusējuma atdalot izvadi (Twig un Latte to dara; izmantojiet šo priekšrocību). Sesijas var palīdzēt pret CSRF, saglabājot CSRF tokenu lietotāja sesijā, kā aprakstīts tālāk. Sagatavotu vaicājumu izmantošana ar PDO — vai SimplePdo palīgmetodes — palīdz novērst SQL injekcijas. CORS var apstrādāt ar vienkāršu āķi pirms Flight::start() izsaukšanas.

Visas šīs metodes darbojas kopā, lai palīdzētu uzturēt jūsu tīmekļa lietojumprogrammas drošas. Jums vienmēr priekšplānā jābūt apgūt un izprast drošības paraugpraksi. Nelūdziet AI asistentam "atspējot CSP" vai vājināt galvenes tikai tāpēc, lai lapa ielādētos, nesaprotot kompromisu.

Pamata lietošana

Galvenes

HTTP galvenes ir viens no vienkāršākajiem veidiem, kā aizsargāt savas tīmekļa lietojumprogrammas. Varat izmantot galvenes, lai novērstu klikšķināšanas nolaupīšanu (clickjacking), XSS un citus uzbrukumus. Ir vairāki veidi, kā pievienot šīs galvenes savai lietojumprogrammai.

Divas lieliskas vietnes, kur pārbaudīt savu galveņu drošību, ir securityheaders.com un observatory.mozilla.org. Pēc tālāk norādītā koda iestatīšanas jūs varat viegli pārbaudīt, vai jūsu galvenes darbojas, izmantojot šīs divas vietnes.

Skelets ietver App\Middleware\SecurityHeadersMiddleware (CSP ar pieprasījumam atbilstošu nonce, frame opcijām, HSTS un vairāk). Dodiet priekšroku tā apzinātai paplašināšanai, nevis galveņu atspējošanai.

Pievienošana ar rokām

Šīs galvenes varat pievienot manuāli, izmantojot header metodi uz Flight\Response objekta.

// Iestatiet X-Frame-Options galveni, lai novērstu clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');

// Iestatiet Content-Security-Policy galveni, lai novērstu XSS
// Piezīme: šī galvene var kļūt ļoti sarežģīta, tāpēc jūs vēlēsities
//  apskatīt piemērus internetā savai lietojumprogrammai
Flight::response()->header("Content-Security-Policy", "default-src 'self'");

// Iestatiet X-XSS-Protection galveni, lai novērstu XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');

// Iestatiet X-Content-Type-Options galveni, lai novērstu MIME uzminēšanu
Flight::response()->header('X-Content-Type-Options', 'nosniff');

// Iestatiet Referrer-Policy galveni, lai kontrolētu, cik daudz referrer informācijas tiek nosūtīts
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');

// Iestatiet Strict-Transport-Security galveni, lai piespiestu HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');

// Iestatiet Permissions-Policy galveni, lai kontrolētu, kādas funkcijas un API var tikt izmantotas
Flight::response()->header('Permissions-Policy', 'geolocation=()');

Tās var pievienot savu routes.php vai index.php failu augšpusē.

Pievienošana kā filtrs

Varat tās arī pievienot filtrā/āķī, piemēram:

// Pievienojiet galvenes filtrā
Flight::before('start', function() {
    Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
    Flight::response()->header("Content-Security-Policy", "default-src 'self'");
    Flight::response()->header('X-XSS-Protection', '1; mode=block');
    Flight::response()->header('X-Content-Type-Options', 'nosniff');
    Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
    Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    Flight::response()->header('Permissions-Policy', 'geolocation=()');
});

Pievienošana kā starpprogrammatūra

Varat tās pievienot arī kā starpprogrammatūras klasi, kas nodrošina vislielāko elastību, nosakot, kuriem maršrutiem to piemērot. Parasti šīs galvenes būtu jāpiemēro visām HTML un API atbildēm.

Skeleta stila ceļš un nosaukumvieta (mapes reģistrs atbilst App\Middleware):

// app/Middleware/SecurityHeadersMiddleware.php

namespace App\Middleware;

use flight\Engine;

class SecurityHeadersMiddleware
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function before(array $params): void
    {
        $response = $this->app->response();
        // Dodiet priekšroku CSP nonce no bootstrap, ja jums ir iekļauti skripti (skelets iestata csp_nonce)
        $nonce = $this->app->get('csp_nonce');
        $csp = $nonce
            ? "default-src 'self'; script-src 'self' 'nonce-{$nonce}'; style-src 'self' 'nonce-{$nonce}'"
            : "default-src 'self'";

        $response->header('X-Frame-Options', 'SAMEORIGIN');
        $response->header('Content-Security-Policy', $csp);
        $response->header('X-XSS-Protection', '1; mode=block');
        $response->header('X-Content-Type-Options', 'nosniff');
        $response->header('Referrer-Policy', 'no-referrer-when-downgrade');
        $response->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
        $response->header('Permissions-Policy', 'geolocation=()');
    }
}

// app/config/routes.php — tukšas virknes grupa = globālā starpprogrammatūra visiem maršrutiem
use App\Middleware\SecurityHeadersMiddleware;
use flight\net\Router;

$router->group('', function (Router $router) {
    $router->get('/users', [ \App\Controller\UserController::class, 'getUsers' ]);
    // vairāk maršrutu
}, [SecurityHeadersMiddleware::class]);

Vecāki projekti joprojām var izmantot app/middlewares un app\middlewares; tas darbojas, ja mapes sakrīt. Jaunās skeleta lietotnes izmanto app/Middleware/ un App\Middleware. Skatīt Automātiskā ielāde.

Starpsaites pieprasījuma viltošana (CSRF)

Starpsaites pieprasījuma viltošana (CSRF) ir uzbrukuma veids, kurā ļaunprātīga vietne var likt lietotāja pārlūkprogrammai nosūtīt pieprasījumu jūsu vietnei. To var izmantot, lai veiktu darbības jūsu vietnē bez lietotāja ziņas. Flight nepiedāvā iebūvētu CSRF aizsardzības mehānismu, taču jūs to viegli varat ieviest pats, izmantojot starpprogrammatūru.

Iestatīšana

Vispirms jums ir jāģenerē CSRF tokens un jāsaglabā tas lietotāja sesijā. Pēc tam varat izmantot šo tokenu savās veidlapās un pārbaudīt to, kad veidlapa tiek iesniegta. Mēs izmantosim flightphp/session spraudni, lai pārvaldītu sesijas.

// Ģenerējiet CSRF tokenu un saglabājiet to lietotāja sesijā
// (pieņemot, ka esat izveidojis sesijas objektu un pievienojis to Flight)
// skatiet sesijas dokumentāciju, lai iegūtu vairāk informācijas
Flight::register('session', flight\Session::class);

// Jums ir jāģenerē tikai viens tokens katrā sesijā (lai tas darbotos 
// vairākās cilnēs un pieprasījumos vienam un tam pašam lietotājam)
if(Flight::session()->get('csrf_token') === null) {
    Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
Izmantojot noklusējuma PHP Flight veidni
<!-- Izmantojiet CSRF tokenu savā veidlapā -->
<form method="post">
    <input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
    <!-- citi veidlapas lauki -->
</form>
Izmantojot Twig (skeleta noklusējums)

Reģistrējiet Twig funkciju vai nododiet tokenu katrā veidlapas skatā. Minimāls piemērs ar globālo mainīgo + veidlapas lauku:

// Konfigurējot Twig (piem., services.php)
$twig->addGlobal('csrf_token', $app->session()->get('csrf_token'));
{# app/views/form.twig #}
<form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
    {# citi lauki #}
</form>
Izmantojot Latte

Varat arī iestatīt pielāgotu funkciju, lai izvadītu CSRF tokenu savās Latte veidnēs.


Flight::map('render', function(string $template, array $data, ?string $block): void {
    $latte = new Latte\Engine;

    // citas konfigurācijas...

    // Iestatiet pielāgotu funkciju, lai izvadītu CSRF tokenu
    $latte->addFunction('csrf', function() {
        $csrfToken = Flight::session()->get('csrf_token');
        return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
    });

    $latte->render($finalPath, $data, $block);
});

Un tagad savās Latte veidnēs varat izmantot csrf() funkciju, lai izvadītu CSRF tokenu.

<form method="post">
    {csrf()}
    <!-- citi veidlapas lauki -->
</form>

CSRF tokena pārbaude

CSRF tokenu var pārbaudīt vairākos veidos.

Starpprogrammatūra
// app/Middleware/CsrfMiddleware.php

namespace App\Middleware;

use flight\Engine;

class CsrfMiddleware
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function before(array $params): void
    {
        if($this->app->request()->method == 'POST') {
            $token = $this->app->request()->data->csrf_token;
            if($token !== $this->app->session()->get('csrf_token')) {
                $this->app->halt(403, 'Invalid CSRF token');
            }
        }
    }
}

// routes.php
use App\Middleware\CsrfMiddleware;

$router->group('', function ($router) {
    $router->get('/users', [ \App\Controller\UserController::class, 'getUsers' ]);
    // vairāk maršrutu
}, [CsrfMiddleware::class]);
Notikumu filtri
// Šī starpprogrammatūra pārbauda, vai pieprasījums ir POST pieprasījums, un, ja ir, tā pārbauda, vai CSRF tokens ir derīgs
Flight::before('start', function() {
    if(Flight::request()->method == 'POST') {

        // iegūstiet csrf tokenu no veidlapas vērtībām
        $token = Flight::request()->data->csrf_token;
        if($token !== Flight::session()->get('csrf_token')) {
            Flight::halt(403, 'Invalid CSRF token');
            // vai JSON atbildei
            Flight::jsonHalt(['error' => 'Invalid CSRF token'], 403);
        }
    }
});

Starpsaites skriptošana (XSS)

Starpsaites skriptošana (XSS) ir uzbrukuma veids, kurā ļaunprātīgs veidlapas ievads var ievietot kodu jūsu vietnē. Lielākā daļa šo iespēju rodas no veidlapas vērtībām, ko aizpilda jūsu galalietotāji. Jums nekad nevajadzētu uzticēties lietotāju ievadei! Vienmēr pieņemiet, ka viņi visi ir labākie hakeri pasaulē. Viņi var ievietot ļaunprātīgu JavaScript vai HTML jūsu lapā. Šo kodu var izmantot, lai nozagtu informāciju no jūsu lietotājiem vai veiktu darbības jūsu vietnē. Izmantojot Flight skatu klasi vai veidņu dzinēju, piemēram, Twig vai Latte, jūs varat viegli aizsargāt izvadi, lai novērstu XSS uzbrukumus.

// Pieņemsim, ka lietotājs ir viltīgs un mēģina to izmantot kā savu vārdu
$name = '<script>alert("XSS")</script>';

// Tas atdalīs izvadi (escape)
Flight::view()->set('name', $name);
// Tas izvadīs: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// Twig (skeleta noklusējums) un Latte pēc noklusējuma automātiski atdala izvadi — dodiet tiem priekšroku, nevis neapstrādātam PHP echo
Flight::render('template', ['name' => $name]);
// Twig: {{ name }}  → atdalīts
// Izvairieties no |raw / neatdalītas izvades, ja vien saturs nav pilnībā uzticams

SQL injekcija

SQL injekcija ir uzbrukuma veids, kurā ļaunprātīgs lietotājs var ievietot SQL kodu jūsu datubāzē. To var izmantot, lai nozagtu informāciju no jūsu datubāzes vai veiktu darbības jūsu datubāzē. Atkal jums nekad nevajadzētu uzticēties lietotāju ievadei! Vienmēr pieņemiet, ka viņi ir izslāpuši pēc asinīm. Izmantojiet sagatavotus vaicājumus — SimplePdo palīgmetodes padara to par noklusējuma ceļu.

// Pieņemot, ka Flight::db() ir reģistrēts kā SimplePdo (vai injicējiet SimplePdo kontrolierī)
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();

// SimplePdo (ieteicams) — vienas rindas vaicājumi ar piesaistītiem parametriem
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);

// Tā pati ideja ar ? vietturiem
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);

Skeleta stila kontrolieros dodiet priekšroku konstruktora injekcijai ar SimplePdo, nevis Flight::db(), lai testi un AI ģenerēts kods paliktu konsekventi (DIC).

Nedrošs piemērs

Tālāk redzams, kāpēc mēs izmantojam SQL sagatavotus vaicājumus, lai aizsargātos no nekaitīgiem piemēriem, kā zemāk:

// galalietotājs aizpilda tīmekļa veidlapu.
// kā veidlapas vērtību hakeris ievada aptuveni šo:
$username = "' OR 1=1; -- ";

$sql = "SELECT * FROM users WHERE username = '$username' LIMIT 5";
$users = Flight::db()->fetchAll($sql);
// Pēc vaicājuma izveides tas izskatās šādi
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5

// Tas izskatās dīvaini, bet tas ir derīgs vaicājums, kas darbosies. Patiesībā,
// tas ir ļoti izplatīts SQL injekcijas uzbrukums, kas atgriezīs visus lietotājus.

var_dump($users); // tas izdrukās visus datubāzes lietotājus, nevis tikai vienu konkrēto lietotājvārdu

Noslēpumi un konfigurācija

JSONP atzvanīšanas validācija

Ja izmantojat Flight Flight::jsonp() metodi, ņemiet vērā, ka Flight pārbauda JSONP atzvanīšanas parametra nosaukumu pret stingru atļauto sarakstu ar regulāro izteiksmi (/^[A-Za-z_$][\w$.]{0,127}$/). Jebkurš atzvanīšanas nosaukums, kas neatbilst šim paraugam, liks Flight izmest izņēmumu, tādējādi novēršot patvaļīga JavaScript ievadīšanu caur ļaunprātīgu atzvanīšanas vērtību.

Šī validācija ir iebūvēta un neprasa papildu konfigurāciju, taču ir vērts par to zināt, atkļūdojot negaidītas kļūdas no JSONP galapunktiem.

CORS

Cross-Origin Resource Sharing (CORS) ir mehānisms, kas ļauj daudzus resursus (piemēram, fontus, JavaScript u.c.) tīmekļa lapā pieprasīt no cita domēna ārpus domēna, no kura resurss nācis. Flight nav iebūvētas funkcionalitātes, taču to var viegli apstrādāt ar āķi, kas tiek palaists pirms Flight::start() metodes izsaukšanas.

// app/Utils/CorsUtil.php  (skelets: PascalCase Utils mape → App\Utils)

namespace App\Utils;

use flight\Engine;

class CorsUtil
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function set(array $params = []): void
    {
        $request = $this->app->request();
        $response = $this->app->response();
        if ($request->getVar('HTTP_ORIGIN') !== '') {
            $this->allowOrigins();
            $response->header('Access-Control-Allow-Credentials', 'true');
            $response->header('Access-Control-Max-Age', '86400');
        }

        if ($request->method === 'OPTIONS') {
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
                $response->header(
                    'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
                );
            }
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
                $response->header(
                    "Access-Control-Allow-Headers",
                    $request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
                );
            }

            $response->status(200);
            $response->send();
            exit;
        }
    }

    private function allowOrigins(): void
    {
        // pielāgojiet savus atļauto resursdatoru sarakstu šeit.
        $allowed = [
            'capacitor://localhost',
            'ionic://localhost',
            'http://localhost',
            'http://localhost:4200',
            'http://localhost:8080',
            'http://localhost:8100',
        ];

        $request = $this->app->request();

        if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
            $response = $this->app->response();
            $response->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
        }
    }
}

// bootstrap / maršruti — izpildīt pirms start
$app = Flight::app();
$cors = new \App\Utils\CorsUtil($app);
$app->before('start', [ $cors, 'set' ]);

Flight konfigurācijas nostiprināšana

Flight atklāj vairākus dzinēja iestatījumus, kuriem ir tieša ietekme uz drošību. Pareiza šo iestatījumu konfigurēšana ir viens no vienkāršākajiem veidiem, kā nostiprināt savu lietojumprogrammu.

flight.allow_method_override

Pēc noklusējuma Flight ļauj klientiem mainīt pieprasījuma HTTP metodi, izmantojot vai nu X-HTTP-Method-Override galveni, vai _method lauku POST pamattekstā. Lai gan tas ir ērti HTML veidlapām, kas var nosūtīt tikai GET/POST, tas var būt bīstami, ja jūs to negaidāt — uzbrucējs varētu viltot DELETE vai PUT pieprasījumus caur parastu veidlapu.

Ja jūsu lietojumprogramma nepaļaujas uz šo uzvedību (piem., veidojat API, ko izmanto mūsdienīgi klienti vai JavaScript priekšgali, kas var nosūtīt jebkuru HTTP metodi), jums tas būtu jāatspējo:

// Savā index.php vai bootstrap failā, pirms Flight::start()
Flight::set('flight.allow_method_override', false);

Noklusējuma vērtība ir true atpakaļsaderības dēļ, taču ieteicams to iestatīt uz false jebkurai lietojumprogrammai, kurai nav tieši nepieciešama aizstāšanas funkcija.

flight.debug

Flight ir flight.debug iestatījums, kas nosaka, vai detalizēta kļūdas informācija (izņēmuma ziņojums, kods un pilna steka izsekojums) tiek parādīta pārlūkprogrammā, kad rodas neapstrādāts izņēmums. Noklusējums ir false, kas nozīmē, ka tiek rādīts tikai vispārīgs ziņojums 500 Internal Server Error — klientam netiek nopludinātas iekšējās detaļas.

Nekad neiespējojiet to uz produkcijas servera. Izmantojiet to tikai lokāli vai staging vidē:

// Drošs tikai lokālai izstrādei — NEKAD produkcijā
Flight::set('flight.debug', true);

Kad flight.debug ir false (noklusējums), jūs joprojām varat reģistrēt kļūdas, iespējojot flight.log_errors:

// Reģistrējiet kļūdas servera pusē, nepadarot tās redzamas klientam
Flight::set('flight.debug', false);
Flight::set('flight.log_errors', true);

Ieteicamā produkcijas konfigurācija

// index.php vai lietots no lietotnes konfigurācijas / bootstrap
Flight::set('flight.allow_method_override', false);
Flight::set('flight.debug', false);
Flight::set('flight.log_errors', true);

Kļūdu apstrāde

Slēpiet sensitīvas kļūdu detaļas produkcijas vidē, lai neizpaustu informāciju uzbrucējiem. Produkcijas vidē reģistrējiet kļūdas, nevis rādiet tās, iestatot display_errors uz 0.

// Savā bootstrap.php vai index.php failā

// pievienojiet to savam app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
    ini_set('display_errors', 0); // Atspējo kļūdu attēlošanu
    ini_set('log_errors', 1);     // Tā vietā reģistrē kļūdas
    ini_set('error_log', '/path/to/error.log');
}

// Savos maršrutos vai kontrolieros
// Izmantojiet Flight::halt() kontrolētām kļūdu atbildēm
Flight::halt(403, 'Access denied');

Ievades sanitizācija

Nekad neuzticieties lietotāja ievadei. Sanitizējiet to, izmantojot filter_var, pirms apstrādes, lai novērstu ļaunprātīgu datu iekļūšanu. Dodiet priekšroku ievades lasīšanai ar $app->request() (vai Flight::request()), nevis neapstrādātiem $_GET / $_POST lietotnes kodā.


// Pieņemsim, ka ir $_POST pieprasījums ar $_POST['input'] un $_POST['email']

// Sanitizējiet virknes ievadi
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Sanitizējiet e-pasta adresi
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);

Paroļu hešēšana

Glabājiet paroles droši un pārbaudiet tās drošā veidā, izmantojot PHP iebūvētās funkcijas, piemēram, password_hash un password_verify. Paroles nekad nedrīkst glabāt vienkāršā tekstā, un tās nedrīkst šifrēt ar atgriezeniskām metodēm. Hešēšana nodrošina, ka pat tad, ja jūsu datubāze tiek kompromitēta, faktiskās paroles paliek aizsargātas.

$password = Flight::request()->data->password;
// Hešējiet paroli, kad to saglabājat (piem., reģistrācijas laikā)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);

// Pārbaudiet paroli (piem., pieteikšanās laikā)
if (password_verify($password, $stored_hash)) {
    // Parole atbilst
}

Pieprasījumu ātruma ierobežošana

Aizsargājieties pret brutāla spēka uzbrukumiem vai pakalpojuma atteikuma uzbrukumiem, ierobežojot pieprasījumu ātrumu ar kešatmiņu.

// Pieņemot, ka flightphp/cache ir instalēts un reģistrēts
// Izmantojot flightphp/cache filtrā
Flight::before('start', function() {
    $cache = Flight::cache();
    $ip = Flight::request()->ip;
    $key = "rate_limit_{$ip}";
    $attempts = (int) $cache->retrieve($key);

    if ($attempts >= 10) {
        Flight::halt(429, 'Too many requests');
    }

    $cache->set($key, $attempts + 1, 60); // Atiestatīt pēc 60 sekundēm
});

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/routing

Maršrutēšana

Pārskats

Maršrutēšana Flight PHP kartē URL modeļus ar atzvanīšanas funkcijām vai klašu metodēm, nodrošinot ātru un vienkāršu pieprasījumu apstrādi. Tā ir veidota ar minimālu papildu slogu, iesācējiem draudzīgu lietošanu un paplašināmību bez ārējām atkarībām.

Izpratne

Maršrutēšana ir pamata mehānisms, kas savieno HTTP pieprasījumus ar jūsu lietojumprogrammas loģiku Flight. Definējot maršrutus, jūs norādāt, kā dažādi URL aktivizē konkrētu kodu — neatkarīgi no tā, vai tas notiek caur funkcijām, klašu metodēm vai kontrolieru darbībām. Flight maršrutēšanas sistēma ir elastīga, atbalstot pamata modeļus, nosauktus parametrus, regulārās izteiksmes un papildu funkcijas, piemēram, atkarību ievadīšanu un resursu maršrutēšanu. Šī pieeja uztur jūsu kodu organizētu un viegli uzturamu, vienlaikus paliekot ātra un vienkārša iesācējiem, kā arī paplašināma pieredzējušiem lietotājiem.

Piezīme: Vēlaties saprast vairāk par maršrutēšanu? Apskatiet lapu "kāpēc ietvars?", lai iegūtu padziļinātu skaidrojumu.

Pamata lietošana

Vienkārša maršruta definēšana

Pamata maršrutēšana Flight tiek veikta, saskaņojot URL modeli ar atzvanīšanas funkciju vai klases un metodes masīvu.

Flight::route('/', function(){
    echo 'hello world!';
});

Maršruti tiek saskaņoti to definēšanas secībā. Pirmais maršruts, kas atbilst pieprasījumam, tiks izsaukts.

Funkciju izmantošana kā atzvanīšanas

Atzvanīšana var būt jebkurš objekts, kas ir izsaucams. Tātad varat izmantot parasto funkciju:

function hello() {
    echo 'hello world!';
}

Flight::route('/', 'hello');

Klašu un metožu izmantošana kā kontrolieris

Varat izmantot arī klases metodi (statisku vai nestatisku):

class GreetingController {
    public function hello() {
        echo 'hello world!';
    }
}

Flight::route('/', [ 'GreetingController','hello' ]);
// vai
Flight::route('/', [ GreetingController::class, 'hello' ]); // ieteicamā metode
// vai
Flight::route('/', [ 'GreetingController::hello' ]);
// vai 
Flight::route('/', [ 'GreetingController->hello' ]);

Vai arī izveidojot objektu vispirms un pēc tam izsaucot metodi:

use flight\Engine;

// GreetingController.php
class GreetingController
{
    protected Engine $app
    public function __construct(Engine $app) {
        $this->app = $app;
        $this->name = 'John Doe';
    }

    public function hello() {
        echo "Hello, {$this->name}!";
    }
}

// index.php
$app = Flight::app();
$greeting = new GreetingController($app);

Flight::route('/', [ $greeting, 'hello' ]);

Piezīme: Pēc noklusējuma, kad kontrolieris tiek izsaukts ietvara ietvaros, flight\Engine klase vienmēr tiek ievadīta, ja vien nenorādāt citādi, izmantojot atkarību ievadīšanas konteineru

Metodei specifiska maršrutēšana

Pēc noklusējuma maršruta modeļi tiek saskaņoti ar visām pieprasījuma metodēm. Varat atbildēt uz konkrētām metodēm, novietojot identifikatoru pirms URL.

Flight::route('GET /', function () {
  echo 'I received a GET request.';
});

Flight::route('POST /', function () {
  echo 'I received a POST request.';
});

// Jūs nevarat izmantot Flight::get() maršrutiem, jo tā ir metode
//    mainīgo iegūšanai, nevis maršruta izveidei.
Flight::post('/', function() { /* kods */ });
Flight::patch('/', function() { /* kods */ });
Flight::put('/', function() { /* kods */ });
Flight::delete('/', function() { /* kods */ });

Varat arī kartēt vairākas metodes vienai atzvanīšanai, izmantojot atdalītāju |:

Flight::route('GET|POST /', function () {
  echo 'I received either a GET or a POST request.';
});

Īpaša apstrāde HEAD un OPTIONS pieprasījumiem

Flight nodrošina iebūvētu apstrādi HEAD un OPTIONS HTTP pieprasījumiem:

HEAD pieprasījumi

Flight::route('GET /info', function() {
    echo 'This is some info!';
});
// HEAD pieprasījums uz /info atgriezīs tās pašas galvenes, bet bez pamatteksta.

OPTIONS pieprasījumi

OPTIONS pieprasījumus Flight automātiski apstrādā jebkuram definētam maršrutam.

// Maršrutam, kas definēts kā:
Flight::route('GET|POST /users', function() { /* ... */ });

// OPTIONS pieprasījums uz /users atbildēs ar:
//
// Statuss: 204 No Content
// Allow: GET, POST, HEAD, OPTIONS

Router objekta izmantošana

Papildus varat iegūt Router objektu, kuram ir dažas palīgmetodes, ko varat izmantot:


$router = Flight::router();

// kartē visas metodes tāpat kā Flight::route()
$router->map('/', function() {
    echo 'hello world!';
});

// GET pieprasījums
$router->get('/users', function() {
    echo 'users';
});
$router->post('/users',             function() { /* kods */});
$router->put('/users/update/@id',   function() { /* kods */});
$router->delete('/users/@id',       function() { /* kods */});
$router->patch('/users/@id',        function() { /* kods */});

Regulārās izteiksmes (Regex)

Maršrutos varat izmantot regulārās izteiksmes:

Flight::route('/user/[0-9]+', function () {
  // Šis atbilst /user/1234
});

Lai gan šī metode ir pieejama, ieteicams izmantot nosauktus parametrus vai nosauktus parametrus ar regulārajām izteiksmēm, jo tie ir lasāmāki un vieglāk uzturami.

Nosauktie parametri

Maršrutos varat norādīt nosauktus parametrus, kas tiks nodoti jūsu atzvanīšanas funkcijai. Tas vairāk ir paredzēts maršruta lasāmībai nekā kam citam. Lūdzu, skatiet sadaļu zemāk par svarīgu piezīmi.

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "hello, $name ($id)!";
});

Varat iekļaut arī regulārās izteiksmes ar saviem nosauktajiem parametriem, izmantojot atdalītāju ::

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // Šis atbilst /bob/123
  // Bet neatbilst /bob/12345
});

Piezīme: Regulārās izteiksmes grupu () saskaņošana ar pozicionālajiem parametriem netiek atbalstīta. Piemēram: :'\(

Svarīga piezīme

Lai gan iepriekšējā piemērā šķiet, ka @name ir tieši saistīts ar mainīgo $name, tas tā nav. Parametru secība atzvanīšanas funkcijā nosaka to, kas tiek nodots. Ja jūs apmainītu parametru secību atzvanīšanas funkcijā, mainīgie tiktu apmainīti arī. Šeit ir piemērs:

Flight::route('/@name/@id', function (string $id, string $name) {
  echo "hello, $name ($id)!";
});

Un, ja jūs dotos uz šādu URL: /bob/123, izvade būtu hello, 123 (bob)!. Esiet uzmanīgi, kad veidojat savus maršrutus un atzvanīšanas funkcijas!

Neobligātie parametri

Varat norādīt nosauktus parametrus, kas nav obligāti saskaņošanai, iekļaujot segmentus iekavās.

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // Šis atbilst šādiem URL:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

Jebkuri neobligātie parametri, kas netiek saskaņoti, tiks nodoti kā NULL.

Aizstājējzīmju maršrutēšana

Saskaņošana tiek veikta tikai atsevišķiem URL segmentiem. Ja vēlaties saskaņot vairākus segmentus, varat izmantot aizstājējzīmi *.

Flight::route('/blog/*', function () {
  // Šis atbilst /blog/2000/02/01
});

Lai novirzītu visus pieprasījumus uz vienu atzvanīšanu, varat rīkoties šādi:

Flight::route('*', function () {
  // Dariet kaut ko
});

404 Nav atrasts apstrādātājs

Pēc noklusējuma, ja URL nevar atrast, Flight nosūtīs HTTP 404 Not Found atbildi, kas ir ļoti vienkārša un parasta. Ja vēlaties pielāgotāku 404 atbildi, varat kartēt savu notFound metodi:

Flight::map('notFound', function() {
    $url = Flight::request()->url;

    // Varat arī izmantot Flight::render() ar pielāgotu veidni.
    $output = <<<HTML
        <h1>Mana pielāgotā 404 Nav atrasts</h1>
        <h3>Lapa, kuru pieprasījāt ({$url}), netika atrasta.</h3>
        HTML;

    $this->response()
        ->clearBody()
        ->status(404)
        ->write($output)
        ->send();
});

Metode nav atrasta apstrādātājs

Pēc noklusējuma, ja URL tiek atrasts, bet metode nav atļauta, Flight nosūtīs HTTP 405 Method Not Allowed atbildi, kas ir ļoti vienkārša un parasta (piem., Method Not Allowed. Allowed Methods are: GET, POST). Tā arī iekļaus Allow galveni ar atļautajām metodēm šim URL.

Ja vēlaties pielāgotāku 405 atbildi, varat kartēt savu methodNotFound metodi:

use flight\net\Route;

Flight::map('methodNotFound', function(Route $route) {
    $url = Flight::request()->url;
    $methods = implode(', ', $route->methods);

    // Varat arī izmantot Flight::render() ar pielāgotu veidni.
    $output = <<<HTML
        <h1>Mana pielāgotā 405 Metode nav atļauta</h1>
        <h3>Metode, kuru pieprasījāt ({$url}), nav atļauta.</h3>
        <p>Atļautās metodes ir: {$methods}</p>
        HTML;

    $this->response()
        ->clearBody()
        ->status(405)
        ->setHeader('Allow', $methods)
        ->write($output)
        ->send();
});

Papildu lietošana

Atkarību ievadīšana maršrutos

Ja vēlaties izmantot atkarību ievadīšanu caur konteineru (PSR-11, PHP-DI, Dice utt.), vienīgais maršrutu veids, kur tas ir pieejams, ir vai nu pašam tieši izveidot objektu un izmantot konteineru sava objekta izveidei, vai arī varat izmantot virknes, lai definētu klasi un izsaucamo metodi. Plašāku informāciju skatiet lapā Atkarību ievadīšana.

Šeit ir ātrs piemērs:


use flight\database\SimplePdo;

// Greeting.php
class Greeting
{
    protected SimplePdo $db;
    public function __construct(SimplePdo $db) {
        $this->db = $db;
    }

    public function hello(int $id) {
        // darīt kaut ko ar $this->db
        $name = $this->db->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
        echo "Hello, world! My name is {$name}!";
    }
}

// index.php

// Iestatiet konteineru ar nepieciešamajiem parametriem
// Skatiet lapu Par atkarību ievadīšanu, lai iegūtu vairāk informācijas par PSR-11
$dice = new \Dice\Dice();

// Neaizmirstiet pārdefinēt mainīgo ar '$dice = '!!!!!
$dice = $dice->addRule(SimplePdo::class, [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// Reģistrējiet konteinera apstrādātāju
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Maršruti kā parasti
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// vai
Flight::route('/hello/@id', 'Greeting->hello');
// vai
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

Izpildes nodošana nākamajam maršrutam

Novecojis Varat nodot izpildi nākamajam atbilstošajam maršrutam, atgriežot true no savas atzvanīšanas funkcijas.

Flight::route('/user/@name', function (string $name) {
  // Pārbaudiet kādu nosacījumu
  if ($name !== "Bob") {
    // Turpiniet uz nākamo maršrutu
    return true;
  }
});

Flight::route('/user/*', function () {
  // Šis tiks izsaukts
});

Tagad ieteicams izmantot starpprogrammatūru, lai apstrādātu sarežģītus gadījumus, piemēram, šo.

Maršruta aizstājvārdi

Piešķirot maršrutam aizstājvārdu, vēlāk varat šo aizstājvārdu dinamiski izsaukt savā lietotnē, lai to ģenerētu vēlāk kodā (piem., saite HTML veidnē vai pāradresācijas URL ģenerēšana).

Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// vai 
Flight::route('/users/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');

// vēlāk kaut kur kodā
class UserController {
    public function update() {

        // kods lietotāja saglabāšanai...
        $id = $user['id']; // piemēram, 5

        $redirectUrl = Flight::getUrl('user_view', [ 'id' => $id ]); // atgriezīs '/users/5'
        Flight::redirect($redirectUrl);
    }
}

Tas ir īpaši noderīgi, ja jūsu URL gadās mainīties. Iepriekšējā piemērā pieņemsim, ka lietotāji tika pārvietoti uz /admin/users/@id vietā. Ar aizstājvārdu maršrutam jums vairs nav jāatrod visi vecie URL savā kodā un jāmaina tie, jo aizstājvārds tagad atgriezīs /admin/users/5, kā iepriekšējā piemērā.

Maršruta aizstājvārdi darbojas arī grupās:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
    // vai
    Flight::route('/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
});

Maršruta informācijas apskate

Ja vēlaties apskatīt atbilstošā maršruta informāciju, to var izdarīt divos veidos:

  1. Varat izmantot rekvizītu executedRoute uz Flight::router() objekta.
  2. Varat pieprasīt, lai maršruta objekts tiktu nodots jūsu atzvanīšanai, nododot true kā trešo parametru maršruta metodē. Maršruta objekts vienmēr būs pēdējais parametrs, kas tiek nodots jūsu atzvanīšanas funkcijai.

executedRoute

Flight::route('/', function() {
  $route = Flight::router()->executedRoute;
  // Dariet kaut ko ar $route
  // Saskaņoto HTTP metožu masīvs
  $route->methods;

  // Nosaukto parametru masīvs
  $route->params;

  // Atbilstošā regulārā izteiksme
  $route->regex;

  // Satur jebkura '*' saturu, kas izmantots URL modelī
  $route->splat;

  // Parāda URL ceļu... ja jums tiešām tas ir nepieciešams
  $route->pattern;

  // Parāda, kāda starpprogrammatūra ir piešķirta šim
  $route->middleware;

  // Parāda šim maršrutam piešķirto aizstājvārdu
  $route->alias;
});

Piezīme: Rekvizīts executedRoute tiks iestatīts tikai pēc tam, kad maršruts ir izpildīts. Ja mēģināsiet tam piekļūt pirms maršruta izpildes, tas būs NULL. Varat izmantot executedRoute arī starpprogrammatūrā!

Nododiet true maršruta definīcijā

Flight::route('/', function(\flight\net\Route $route) {
  // Saskaņoto HTTP metožu masīvs
  $route->methods;

  // Nosaukto parametru masīvs
  $route->params;

  // Atbilstošā regulārā izteiksme
  $route->regex;

  // Satur jebkura '*' saturu, kas izmantots URL modelī
  $route->splat;

  // Parāda URL ceļu... ja jums tiešām tas ir nepieciešams
  $route->pattern;

  // Parāda, kāda starpprogrammatūra ir piešķirta šim
  $route->middleware;

  // Parāda šim maršrutam piešķirto aizstājvārdu
  $route->alias;
}, true);// <-- Šis true parametrs to nodrošina

Maršrutu grupēšana un starpprogrammatūra

Var būt gadījumi, kad vēlaties grupēt saistītus maršrutus (piemēram, /api/v1). To var izdarīt, izmantojot metodi group:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // Atbilst /api/v1/users
  });

  Flight::route('/posts', function () {
    // Atbilst /api/v1/posts
  });
});

Varat pat ligzdot grupu grupas:

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() iegūst mainīgos, tas neiestata maršrutu! Skatiet objekta kontekstu zemāk
    Flight::route('GET /users', function () {
      // Atbilst GET /api/v1/users
    });

    Flight::post('/posts', function () {
      // Atbilst POST /api/v1/posts
    });

    Flight::put('/posts/1', function () {
      // Atbilst PUT /api/v1/posts
    });
  });
  Flight::group('/v2', function () {

    // Flight::get() iegūst mainīgos, tas neiestata maršrutu! Skatiet objekta kontekstu zemāk
    Flight::route('GET /users', function () {
      // Atbilst GET /api/v2/users
    });
  });
});

Grupēšana ar objekta kontekstu

Varat joprojām izmantot maršrutu grupēšanu ar Engine objektu šādā veidā:

$app = Flight::app();

$app->group('/api/v1', function (Router $router) {

  // izmantojiet $router mainīgo
  $router->get('/users', function () {
    // Atbilst GET /api/v1/users
  });

  $router->post('/posts', function () {
    // Atbilst POST /api/v1/posts
  });
});

Piezīme: Šī ir ieteicamā metode maršrutu un grupu definēšanai ar $router objektu.

Grupēšana ar starpprogrammatūru

Varat arī piešķirt starpprogrammatūru maršrutu grupai:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // Atbilst /api/v1/users
  });
}, [ MyAuthMiddleware::class ]); // vai [ new MyAuthMiddleware() ], ja vēlaties izmantot instanci

Sīkāku informāciju skatiet lapā grupas starpprogrammatūra.

Resursu maršrutēšana

Varat izveidot maršrutu kopu resursam, izmantojot metodi resource. Tas izveidos maršrutu kopu resursam, kas atbilst RESTful konvencijām.

Lai izveidotu resursu, rīkojieties šādi:

Flight::resource('/users', UsersController::class);

Un fonā tiks izveidoti šādi maršruti:

[
      'index' => 'GET /users',
      'create' => 'GET /users/create',
      'store' => 'POST /users',
      'show' => 'GET /users/@id',
      'edit' => 'GET /users/@id/edit',
      'update' => 'PUT /users/@id',
      'destroy' => 'DELETE /users/@id'
]

Un jūsu kontrolieris izmantos šādas metodes:

class UsersController
{
    public function index(): void
    {
    }

    public function show(string $id): void
    {
    }

    public function create(): void
    {
    }

    public function store(): void
    {
    }

    public function edit(string $id): void
    {
    }

    public function update(string $id): void
    {
    }

    public function destroy(string $id): void
    {
    }
}

Piezīme: Jūs varat apskatīt jaunpievienotos maršrutus ar runway, izpildot php runway routes.

Resursu maršrutu pielāgošana

Ir dažas iespējas, kā konfigurēt resursu maršrutus.

Aizstājvārda bāze

Varat konfigurēt aliasBase. Pēc noklusējuma aizstājvārds ir pēdējā norādītā URL daļa. Piemēram, /users/ rezultātā aliasBase būs users. Kad šie maršruti tiek izveidoti, aizstājvārdi ir users.index, users.create utt. Ja vēlaties mainīt aizstājvārdu, iestatiet aliasBase uz vēlamo vērtību.

Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Only un Except

Varat arī norādīt, kurus maršrutus vēlaties izveidot, izmantojot opcijas only un except.

// Iekļaujiet tikai šīs metodes un izslēdziet pārējās
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
// Izslēdziet tikai šīs metodes un iekļaujiet pārējās
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);

Tās būtībā ir iekļaušanas un izslēgšanas opcijas, lai jūs varētu norādīt, kurus maršrutus vēlaties izveidot.

Starpprogrammatūra

Varat arī norādīt starpprogrammatūru, kas jāpalaiž katram maršrutam, kas izveidots ar resource metodi.

Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);

Straumēšanas atbildes

Tagad varat straumēt atbildes klientam, izmantojot stream() vai streamWithHeaders(). Tas ir noderīgi, lai nosūtītu lielus failus, ilgstošus procesus vai ģenerētu lielas atbildes. Maršruta straumēšana tiek apstrādāta nedaudz savādāk nekā parastais maršruts.

Piezīme: Straumēšanas atbildes ir pieejamas tikai tad, ja flight.v2.output_buffering ir iestatīts uz false.

Straumēšana ar manuālām galvenēm

Varat straumēt atbildi klientam, izmantojot metodi stream() maršrutā. Ja to darāt, jums pašam jāiestata visas galvenes pirms jebko izvadāt klientam. Tas tiek darīts ar PHP funkciju header() vai Flight::response()->setRealHeader() metodi.

Flight::route('/@filename', function($filename) {

    $response = Flight::response();

    // protams, jūs sanitizētu ceļu un tamlīdzīgi.
    $fileNameSafe = basename($filename);

    // Ja jums ir papildu galvenes, kas jāiestata pēc maršruta izpildes,
    // tās jādefinē pirms jebkas tiek izvadīts.
    // Tām visām jābūt tiešam izsaukumam uz header() funkciju vai
    // izsaukumam uz Flight::response()->setRealHeader()
    header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
    // vai
    $response->setRealHeader('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');

    $filePath = '/some/path/to/files/'.$fileNameSafe;

    if (!is_readable($filePath)) {
        Flight::halt(404, 'File not found');
    }

    // manuāli iestatiet satura garumu, ja vēlaties
    header('Content-Length: '.filesize($filePath));
    // vai
    $response->setRealHeader('Content-Length: '.filesize($filePath));

    // Straumējiet failu klientam, kamēr tas tiek lasīts
    readfile($filePath);

// Šī ir burvju rindiņa šeit
})->stream();

Straumēšana ar galvenēm

Varat arī izmantot metodi streamWithHeaders(), lai iestatītu galvenes pirms straumēšanas sākšanas.

Flight::route('/stream-users', function() {

    // šeit varat pievienot jebkuras papildu galvenes, kuras vēlaties
    // tikai jāizmanto header() vai Flight::response()->setRealHeader()

    // neatkarīgi no tā, kā iegūstat savus datus, tikai kā piemērs...
    $users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");

    echo '{';
    $user_count = count($users);
    while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
        echo json_encode($user);
        if(--$user_count > 0) {
            echo ',';
        }

        // Tas ir nepieciešams, lai nosūtītu datus klientam
        ob_flush();
    }
    echo '}';

// Šādi jūs iestatīsiet galvenes pirms straumēšanas sākšanas.
})->streamWithHeaders([
    'Content-Type' => 'application/json',
    'Content-Disposition' => 'attachment; filename="users.json"',
    // neobligāts statusa kods, pēc noklusējuma 200
    'status' => 200
]);

Skatīt arī

Problēmu novēršana

404 Nav atrasts vai negaidīta maršruta darbība

Ja redzat 404 Nav atrasts kļūdu (bet jūs zvērat uz savu dzīvību, ka tā tur tiešām ir un tā nav drukas kļūda), tas faktiski var būt problēma ar to, ka maršruta galapunktā atgriežat vērtību, nevis vienkārši to izvadāt. Iemesls tam ir apzināts, bet tas var pārsteigt dažus izstrādātājus.

Flight::route('/hello', function(){
    // Tas var izraisīt 404 Nav atrasts kļūdu
    return 'Hello World';
});

// Tas, ko jūs, iespējams, vēlaties
Flight::route('/hello', function(){
    echo 'Hello World';
});

Iemesls tam ir īpašs mehānisms, kas iebūvēts maršrutētājā un kas apstrādā atgriezto izvadi kā signālu "doties uz nākamo maršrutu". Šo darbību varat redzēt dokumentētu sadaļā Maršrutēšana.

Izmaiņu žurnāls

Learn/learn

Uzziniet par Flight

Flight ir ātrs, vienkāršs un paplašināms PHP ietvars. Tas ir diezgan daudzpusīgs, un to var izmantot jebkura veida tīmekļa lietojumprogrammu izveidei. Tas ir izstrādāts, domājot par vienkāršību, un ir rakstīts tā, lai to būtu viegli saprast un lietot — gan cilvēkiem, gan AI kodēšanas palīgiem.

Piezīme: Jūs redzēsiet piemērus, kuros Flight:: tiek izmantots kā statisks mainīgais, un dažus, kuros tiek izmantots $app-> Engine objekts. Abi savstarpēji aizvietojami. $app un $this->app kontrolierī/starpprogrammatūrā ir Flight komandas ieteiktā pieeja (un to oficiālais skeletons + AGENTS.md nosaka kā standartu jauniem projektiem).

Pamatkomponenti

Maršrutēšana

Uzziniet, kā pārvaldīt maršrutus savā tīmekļa lietojumprogrammā. Tas ietver arī maršrutu grupēšanu, maršrutu parametrus un starpprogrammatūru.

Starpprogrammatūra

Uzziniet, kā izmantot starpprogrammatūru, lai filtrētu pieprasījumus un atbildes savā lietojumprogrammā.

Automātiskā ielāde

Uzziniet, kā automātiski ielādēt savas klases. Mapes reģistram jāatbilst jūsu vārdtelpām—skeletons izmanto App\ un PascalCase mapes, piemēram, app/Controller/.

Pieprasījumi

Uzziniet, kā apstrādāt pieprasījumus un atbildes savā lietojumprogrammā.

Atbildes

Uzziniet, kā nosūtīt atbildes saviem lietotājiem.

HTML veidnes

Uzziniet, kā renderēt HTML, izmantojot Twig (skeletona noklusējums), Latte vai citus dzinējus—ne tikai iebūvētos PHP skatus.

Drošība

Uzziniet, kā aizsargāt savu lietojumprogrammu no izplatītiem drošības apdraudējumiem.

Konfigurācija

Uzziniet, kā konfigurēt ietvaru savai lietojumprogrammai.

Notikumu pārvaldnieks

Uzziniet, kā izmantot notikumu sistēmu, lai savai lietojumprogrammai pievienotu pielāgotus notikumus.

Flight paplašināšana

Uzziniet, kā paplašināt ietvaru, pievienojot savas metodes un klases.

Metodes āķi un filtrēšana

Uzziniet, kā pievienot notikumu āķus savām metodēm un iekšējām ietvara metodēm.

Atkarību injekcijas konteiners (DIC)

Uzziniet, kā izmantot atkarību injekcijas konteinerus (DIC), lai pārvaldītu savas lietojumprogrammas atkarības.

Palīgklases

Kolekcijas

Kolekcijas tiek izmantotas, lai saglabātu datus un būtu pieejamas kā masīvs vai kā objekts, lai atvieglotu lietošanu.

JSON ietinējs

Tajā ir dažas vienkāršas funkcijas, lai jūsu JSON kodēšana un atkodēšana būtu konsekventa.

SimplePdo

PDO reizēm var sagādāt vairāk galvassāpju, nekā nepieciešams. SimplePdo ir mūsdienīga PDO palīgklase ar ērtām metodēm, piemēram, insert(), update(), delete() un transaction(), lai datubāzes darbības būtu daudz vienkāršākas.

PdoWrapper (Novecojis)

Sākotnējais PDO ietinējs ir novecojis no v3.18.0. Lūdzu, tā vietā izmantojiet SimplePdo.

Augšupielādēto failu apstrādātājs

Vienkārša klase, kas palīdz pārvaldīt augšupielādētos failus un pārvietot tos uz pastāvīgu atrašanās vietu.

Svarīgi jēdzieni

Kāpēc ietvars?

Šeit ir īss raksts par to, kāpēc jums vajadzētu izmantot ietvaru. Ir labi saprast ietvaru izmantošanas priekšrocības, pirms sākat to lietot.

Turklāt izcilu apmācību ir izveidojis @lubiana. Lai gan tajā nav detalizēti aprakstīts tieši Flight, šī rokasgrāmata palīdzēs jums saprast dažus galvenos jēdzienus, kas saistīti ar ietvariem, un kāpēc tos ir izdevīgi izmantot. Apmācību varat atrast šeit.

Flight salīdzinājumā ar citiem ietvariem

Ja pārejat no cita ietvara, piemēram, Laravel, Slim, Fat-Free vai Symfony, uz Flight, šī lapa palīdzēs jums saprast atšķirības starp tiem.

Citas tēmas

Vienību testēšana

Sekojiet šai rokasgrāmatai, lai uzzinātu, kā veikt vienību testus savam Flight kodam, lai tas būtu ļoti stabils.

AI un izstrādātāja pieredze

Flight ir veidots, lai sadarbotos ar programmēšanas LLM: AGENTS.md, Runway ai:* komandas un viens skaidrs skeletona izkārtojums, lai aģenti paliktu uz pareizā ceļa.

Pāreja no v2 uz v3

Atpakaļsaderība lielākoties ir saglabāta, taču ir dažas izmaiņas, par kurām jums jāzina, pārejot no v2 uz v3.

Learn/unit_testing

Vienību testēšana

Pārskats

Vienību testēšana Flight lietotnē palīdz nodrošināt, ka jūsu lietotne darbojas, kā paredzēts, agri atklāt kļūdas un padarīt koda bāzi vieglāk uzturējamu. Flight ir izstrādāts, lai nevainojami sadarbotos ar PHPUnit, kas ir populārākais PHP testēšanas ietvars.

Izpratne

Vienību testi pārbauda atsevišķu jūsu lietotnes daļu (piemēram, kontrolleru vai pakalpojumu) uzvedību izolēti. Flight kontekstā tas nozīmē pārbaudīt, kā jūsu maršruti, kontrolleri un loģika reaģē uz dažādiem ievaddatiem, nepaļaujoties uz globālo stāvokli vai reāliem ārējiem pakalpojumiem.

Galvenie principi:

Pamata lietošana

PHPUnit iestatīšana

  1. Instalējiet PHPUnit ar Composer:
    composer require --dev phpunit/phpunit
  2. Izveidojiet tests direktoriju jūsu projekta saknē.
  3. Pievienojiet testa skriptu savam composer.json:
    "scripts": {
        "test": "phpunit --configuration phpunit.xml"
    }
  4. Izveidojiet phpunit.xml failu:
    <?xml version="1.0" encoding="UTF-8"?>
    <phpunit bootstrap="vendor/autoload.php">
        <testsuites>
            <testsuite name="Flight Tests">
                <directory>tests</directory>
            </testsuite>
        </testsuites>
    </phpunit>

Tagad varat palaist savus testus ar composer test.

Vienkārša maršruta apstrādātāja testēšana

Pieņemsim, ka jums ir maršruts, kas validē e-pasta adresi:

// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);

// UserController.php
class UserController {
    protected $app;
    public function __construct(flight\Engine $app) {
        $this->app = $app;
    }
    public function register() {
        $email = $this->app->request()->data->email;
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
        }
        return $this->app->json(['status' => 'success', 'message' => 'Valid email']);
    }
}

Vienkāršs tests šim kontrollerim:

use PHPUnit\Framework\TestCase;
use flight\Engine;

class UserControllerTest extends TestCase {
    public function testValidEmailReturnsSuccess() {
        $app = new Engine();
        $app->request()->data->email = 'test@example.com';
        $controller = new UserController($app);
        $controller->register();
        $response = $app->response()->getBody();
        $output = json_decode($response, true);
        $this->assertEquals('success', $output['status']);
        $this->assertEquals('Valid email', $output['message']);
    }

    public function testInvalidEmailReturnsError() {
        $app = new Engine();
        $app->request()->data->email = 'invalid-email';
        $controller = new UserController($app);
        $controller->register();
        $response = $app->response()->getBody();
        $output = json_decode($response, true);
        $this->assertEquals('error', $output['status']);
        $this->assertEquals('Invalid email', $output['message']);
    }
}

Padomi:

Atkarību injekcijas izmantošana testējamiem kontrolleriem

Ievadiet atkarības (piemēram, datu bāzi vai e-pasta sūtītāju) savos kontrolleros, lai tos būtu viegli mockot testos:

use flight\database\SimplePdo;

class UserController {
    protected $app;
    protected $db;
    protected $mailer;
    public function __construct($app, $db, $mailer) {
        $this->app = $app;
        $this->db = $db;
        $this->mailer = $mailer;
    }
    public function register() {
        $email = $this->app->request()->data->email;
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
        }
        $this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
        $this->mailer->sendWelcome($email);
        return $this->app->json(['status' => 'success', 'message' => 'User registered']);
    }
}

Un tests ar mock objektiem:

use PHPUnit\Framework\TestCase;

class UserControllerDICTest extends TestCase {
    public function testValidEmailSavesAndSendsEmail() {
        $mockDb = $this->createMock(flight\database\SimplePdo::class);
        $mockDb->method('runQuery')->willReturn(true);
        $mockMailer = new class {
            public $sentEmail = null;
            public function sendWelcome($email) { $this->sentEmail = $email; return true; }
        };
        $app = new flight\Engine();
        $app->request()->data->email = 'test@example.com';
        $controller = new UserController($app, $mockDb, $mockMailer);
        $controller->register();
        $response = $app->response()->getBody();
        $result = json_decode($response, true);
        $this->assertEquals('success', $result['status']);
        $this->assertEquals('User registered', $result['message']);
        $this->assertEquals('test@example.com', $mockMailer->sentEmail);
    }
}

Papildu lietošana

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/flight_vs_symfony

Flight pret Symfoniju

Kas ir Symfony?

Symfony ir kopums ar pārlietojamiem PHP komponentiem un PHP ietvars tīmekļa projektiem.

Standarta pamatne, uz kuras tiek izveidotas labākās PHP lietojumprogrammas. Izvēlieties jebkuru no 50 pieejamajiem neatkarīgajiem komponentiem savām lietojumprogrammām.

Paātriniet jūsu PHP tīmekļa lietojumprogrammu izveidi un uzturēšanu. Beidziet atkārtojošos koda rakstīšanas uzdevumus un baudiet kontroles pār savu kodu priekšrocības.

Plusi salīdzinājumā ar Flight

Mīnusi salīdzinājumā ar Flight

Learn/flight_vs_another_framework

Salīdzinot Flight ar citu ietvaru

Ja jūs pārietat no cita ietvara, piemēram, Laravel, Slim, Fat-Free vai Symfony uz Flight, šī lapa palīdzēs jums saprast atšķirības starp abiem.

Laravel

Laravel ir pilnīgi iezīmēts ietvars, kuram ir visas papardes un brīnumaini izstrādātāja vērsti ekosistēma, bet par cenu veiktspējai un sarežģītībai.

Skatiet salīdzinājumu starp Laravel un Flight.

Slim

Slim ir mikro ietvars, kas ir līdzīgs Flight. Tas ir izstrādāts, lai būtu viegls un viegli lietojams, bet var būt nedaudz sarežģītāks nekā Flight.

Skatiet salīdzinājumu starp Slim un Flight.

Fat-Free

Fat-Free ir pilnas kaudzes ietvars daudz mazākā iepakojumā. Lai arī tam ir visas rīces rīku kastē, tas var padarīt dažus projektus sarežģītākus nekā tie vajadzīgi būtu.

Skatiet salīdzinājumu starp Fat-Free un Flight.

Symfony

Symfony ir modulārs uzņēmumu līmeņa ietvars, kas ir izstrādāts, lai būtu elastīgs un skalējams. Mazākiem projektiem vai jaunajiem izstrādātājiem Symfony var būt nedaudz apburošs.

Skatiet salīdzinājumu starp Symfony un Flight.

Learn/pdo_wrapper

PdoWrapper PDO Palīdze ģenerators

BRĪDINĀJUMS

Novecojis: PdoWrapper ir novecojis kopš Flight v3.18.0. Tas netiks noņemts nākamajā versijā, bet tiks uzturēts atpakaļsaderībai. Lūdzu, izmantojiet SimplePdo tā vietā, kas piedāvā tādu pašu funkcionalitāti plus papildu palīgmēģus biežām datubāzes operācijām.

Pārskats

PdoWrapper klase Flight ir draudzīgs palīgs darbam ar datubāzēm, izmantojot PDO. Tā vienkāršo izplatītās datubāzes uzdevumus, pievieno dažas ērtas metodes rezultātu iegūšanai un atgriež rezultātus kā Collections vieglai piekļuvei. Tā arī atbalsta vaicājumu reģistrēšanu un lietojumprogrammas veiktspējas uzraudzību (APM) sarežģītiem gadījumiem.

Saprašana

Darbs ar datubāzēm PHP var būt nedaudz verbāls, īpaši, izmantojot PDO tieši. PdoWrapper paplašina PDO un pievieno metodes, kas padara vaicājumu veikšanu, rezultātu iegūšanu un apstrādi daudz vieglāku. Tā vietā, lai žonglētu ar sagatavotiem paziņojumiem un iegūšanas režīmiem, jūs saņemat vienkāršas metodes izplatītiem uzdevumiem, un katra rindiņa tiek atgriezta kā Collection, tāpēc jūs varat izmantot masīva vai objekta notāciju.

Jūs varat reģistrēt PdoWrapper kā kopīgu servisu Flight, un tad izmantot to jebkur savā lietojumprogrammā caur Flight::db().

Pamata Izmantošana

Reģistrēšana PDO Palīgam

Vispirms reģistrējiet PdoWrapper klasi ar Flight:

Flight::register('db', \flight\database\PdoWrapper::class, [
    'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
]);

Tagad jūs varat izmantot Flight::db() jebkur, lai iegūtu savienojumu ar datubāzi.

Vaicājumu Izpilde

runQuery()

function runQuery(string $sql, array $params = []): PDOStatement

Izmantojiet to INSERT, UPDATE vai kad vēlaties iegūt rezultātus manuāli:

$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM users WHERE status = ?", ['active']);
while ($row = $statement->fetch()) {
    // $row ir masīvs
}

Jūs varat izmantot to arī rakstīšanai:

$db->runQuery("INSERT INTO users (name) VALUES (?)", ['Alice']);
$db->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 1]);

fetchField()

function fetchField(string $sql, array $params = []): mixed

Iegūstiet vienu vērtību no datubāzes:

$count = Flight::db()->fetchField("SELECT COUNT(*) FROM users WHERE status = ?", ['active']);

fetchRow()

function fetchRow(string $sql, array $params = []): Collection

Iegūstiet vienu rindiņu kā Collection (masīva/objekta piekļuve):

$user = Flight::db()->fetchRow("SELECT * FROM users WHERE id = ?", [123]);
echo $user['name'];
// vai
echo $user->name;

fetchAll()

function fetchAll(string $sql, array $params = []): array<Collection>

Iegūstiet visas rindiņas kā Collections masīvu:

$users = Flight::db()->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
foreach ($users as $user) {
    echo $user['name'];
    // vai
    echo $user->name;
}

Izmantojot IN() Vietu Turētājus

Jūs varat izmantot vienu ? IN() klauzulā un nodot masīvu vai komatiem atdalītu virkni:

$ids = [1, 2, 3];
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", [$ids]);
// vai
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", ['1,2,3']);

Uzlabota Izmantošana

Vaicājumu Reģistrēšana & APM

Ja vēlaties izsekot vaicājuma veiktspēju, iespējiet APM izsekošanu reģistrējot:

Flight::register('db', \flight\database\PdoWrapper::class, [
    'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [/* options */], true // pēdējais params iespējina APM
]);

Pēc vaicājumu izpildes jūs varat reģistrēt tos manuāli, bet APM tos reģistrēs automātiski, ja iespējots:

Flight::db()->logQueries();

Tas izraisīs notikumu (flight.db.queries) ar savienojuma un vaicājuma metrikiem, ko jūs varat klausīties, izmantojot Flight notikumu sistēmu.

Pilns Piemērs

Flight::route('/users', function () {
    // Iegūt visus lietotājus
    $users = Flight::db()->fetchAll('SELECT * FROM users');

    // Straumēt visus lietotājus
    $statement = Flight::db()->runQuery('SELECT * FROM users');
    while ($user = $statement->fetch()) {
        echo $user['name'];
    }

    // Iegūt vienu lietotāju
    $user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);

    // Iegūt vienu vērtību
    $count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');

    // Īpaša IN() sintakse
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', ['1,2,3,4,5']);

    // Ievietot jaunu lietotāju
    Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
    $insert_id = Flight::db()->lastInsertId();

    // Atjaunināt lietotāju
    Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);

    // Dzēst lietotāju
    Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);

    // Iegūt skarto rindu skaitu
    $statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
    $affected_rows = $statement->rowCount();
});

Skatīt Arī

Traucējummeklēšana

Izmaiņu Žurnāls

Learn/dependency_injection_container

Atkarību ievadīšanas konteiners

Pārskats

Atkarību ievadīšanas konteiners (DIC) ir jaudīgs papildinājums, kas ļauj pārvaldīt jūsu lietojumprogrammas atkarības. Tas ir arī viens no lielākajiem iemesliem, kāpēc Flight labi sadarbojas ar AI kodēšanas rīkiem un vienību testiem: kontrolleri konstruktorā saņem to, kas tiem nepieciešams, nevis piekļūst globālajiem mainīgajiem.

Izpratne

Atkarību ievadīšana (DI) ir galvenā koncepcija mūsdienu PHP ietvaros un tiek izmantota, lai pārvaldītu objektu izveidi un konfigurāciju. Daži DIC bibliotēku piemēri: flightphp/container, Dice, Pimple, PHP-DI un league/container.

DIC ir elegantīgs veids, kā izveidot un pārvaldīt savas klases centralizētā vietā. Tas ir noderīgi, ja jums viens un tas pats objekts jānodod vairākām klasēm (kontrolleriem, starpprogrammatūrai, komandām utt.).

Oficiālais flightphp/skeleton savieno Dice failā app/config/services.php, aizstāj kopīgo flight\Engine instanci un atrisina maršrutu mērķus, piemēram, [App\Controller\HomeController::class, 'index']. Jauniem projektiem izmantojiet šo paraugu, lai cilvēki un aģenti rediģētu vienas un tās pašas vietas.

Pamata lietošana

Vecais veids, kā to darīt, varētu izskatīties šādi:


require 'vendor/autoload.php';

// klase, lai pārvaldītu lietotājus no datubāzes
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

// jūsu routes.php failā

$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');

$UserController = new UserController($db);
Flight::route('/user/@id', [ $UserController, 'view' ]);
// citi UserController maršruti...

Flight::start();

No augšējā koda var redzēt, ka mēs izveidojam jaunu PDO objektu un nododam to mūsu UserController klasei. Mazai lietojumprogrammai tas ir labi, bet, kad jūsu lietojumprogramma aug, jūs atklāsiet, ka vienu un to pašu PDO objektu izveidojat vai nododat vairākās vietās. Šeit noder DIC.

Šis ir tas pats piemērs, izmantojot DIC (ar Dice):


require 'vendor/autoload.php';

// tā pati klase kā iepriekš. Nekas nav mainījies
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

// izveido jaunu konteineru
$container = new \Dice\Dice;

// pievieno kārtulu, lai pateiktu konteineram, kā izveidot PDO objektu
// neaizmirsti to atkārtoti piešķirt sev, kā parādīts zemāk!
$container = $container->addRule('PDO', [
    // shared nozīmē, ka katru reizi tiks atgriezts tas pats objekts
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// Tas reģistrē konteinera apstrādātāju, lai Flight zinātu to izmantot.
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// tagad mēs varam izmantot konteineru, lai izveidotu mūsu UserController
Flight::route('/user/@id', [ UserController::class, 'view' ]);

Flight::start();

Varbūt jūs domājat, ka piemēram tika pievienots daudz lieka koda. Burvība parādās, kad jums ir cits kontrolleris, kuram nepieciešams PDO objekts.


// Ja visiem jūsu kontrolleriem konstruktorā ir nepieciešams PDO objekts,
// katram no šiem maršrutiem tas tiks automātiski ievadīts!!!
Flight::route('/company/@id', [ CompanyController::class, 'view' ]);
Flight::route('/organization/@id', [ OrganizationController::class, 'view' ]);
Flight::route('/category/@id', [ CategoryController::class, 'view' ]);
Flight::route('/settings', [ SettingsController::class, 'view' ]);

Papildu ieguvums, izmantojot DIC, ir tas, ka vienību testēšana kļūst daudz vienkāršāka. Jūs varat izveidot viltojuma (mock) objektu un nodot to savai klasei. Tas ir milzīgs ieguvums, rakstot testus savai lietojumprogrammai — un, kad AI palīgs ģenerē kontrolleri, konstruktora ievadīšana sniedz tam skaidru, konsekventu paraugu, kam sekot (vienību testēšanas ceļvedis).

Centralizēta DIC apstrādātāja izveide

Jūs varat izveidot centralizētu DIC apstrādātāju savā pakalpojumu failā, paplašinot savu lietojumprogrammu. Šeit ir piemērs:

// services.php

// izveido jaunu konteineru
$container = new \Dice\Dice;
// neaizmirsti to atkārtoti piešķirt sev, kā parādīts zemāk!
$container = $container->addRule('PDO', [
    // shared nozīmē, ka katru reizi tiks atgriezts tas pats objekts
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// tagad mēs varam izveidot kartējamu metodi jebkura objekta izveidei.
Flight::map('make', function($class, $params = []) use ($container) {
    return $container->create($class, $params);
});

// Tas reģistrē konteinera apstrādātāju, lai Flight zinātu to izmantot kontrolleriem/starpprogrammatūrai
Flight::registerContainerHandler(function($class, $params) {
    return Flight::make($class, $params);
});


// pieņemsim, ka mums ir šāda parauga klase, kas konstruktorā saņem PDO objektu
class EmailCron {
    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function send() {
        // kods, kas nosūta e-pastu
    }
}

// Un visbeidzot jūs varat izveidot objektus, izmantojot atkarību ievadīšanu
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();

flightphp/container

Flight ir spraudnis, kas nodrošina vienkāršu PSR-11 saderīgu konteineru, kuru varat izmantot, lai pārvaldītu savu atkarību ievadīšanu. Šeit ir ātrs piemērs, kā to lietot:


// index.php, piemēram
require 'vendor/autoload.php';

use flight\Container;

$container = new Container;

$container->set(PDO::class, fn(): PDO => new PDO('sqlite::memory:'));

Flight::registerContainerHandler([$container, 'get']);

class TestController {
  private PDO $pdo;

  function __construct(PDO $pdo) {
    $this->pdo = $pdo;
  }

  function index() {
    var_dump($this->pdo);
    // izvadīs šo pareizi!
  }
}

Flight::route('GET /', [TestController::class, 'index']);

Flight::start();

Papildu lietošana ar flightphp/container

Jūs varat arī rekursīvi atrisināt atkarības. Šeit ir piemērs:

<?php

require 'vendor/autoload.php';

use flight\Container;

class User {}

interface UserRepository {
  function find(int $id): ?User;
}

class PdoUserRepository implements UserRepository {
  private PDO $pdo;

  function __construct(PDO $pdo) {
    $this->pdo = $pdo;
  }

  function find(int $id): ?User {
    // Implementācija ...
    return null;
  }
}

$container = new Container;

$container->set(PDO::class, static fn(): PDO => new PDO('sqlite::memory:'));
$container->set(UserRepository::class, PdoUserRepository::class);

$userRepository = $container->get(UserRepository::class);
var_dump($userRepository);

/*
object(PdoUserRepository)#4 (1) {
  ["pdo":"PdoUserRepository":private]=>
  object(PDO)#3 (0) {
  }
}
 */

DICE

Jūs varat arī izveidot savu DIC apstrādātāju. Tas ir noderīgi, ja jums ir pielāgots konteiners, kuru vēlaties izmantot un kurš nav PSR-11 (Dice). Skatiet pamata lietošanas sadaļu, lai uzzinātu, kā to izdarīt.

Turklāt ir daži noderīgi noklusējumi, kas atvieglos jūsu darbu, lietojot Flight.

Engine instance (nepieciešama $app ievadīšanai)

Ja kontrolleros vai starpprogrammatūrā norādāt tipu flight\Engine, Dice nedrīkst izveidot jaunu Engine. Aizstājiet to ar to pašu instanci no sāknēšanas faila. To dara oficiālais skeleton, un tas ir paraugs, ko AGENTS.md sagaida no AI ģenerētiem kontrolleriem:

// Kaut kur jūsu sāknēšanas / services.php failā
use flight\Engine;
use flight\database\SimplePdo;

$app = Flight::app(); // vai $engine = Flight::app();

$container = new \Dice\Dice;
$container = $container->addRule('*', [
    'substitutions' => [
        // Svarīgi: atkārtoti izmantot sāknēto Engine — neļaujiet Dice veikt `new Engine()`
        Engine::class => $app,
        // Jaunam kodam vēlams izmantot SimplePdo
        // SimplePdo::class => $db,
        // Config::class => $config,
        // \Twig\Environment::class => $twig,
    ]
]);

$app->registerContainerHandler(function ($class, $params) use ($container) {
    return $container->create($class, $params);
});

// Neobligāts palīgs maršrutu ārpus koda
$app->map('make', function ($class, $params = []) use ($container) {
    return $container->create($class, $params);
});
// app/Controller/MyController.php  (skeleton izkārtojums — mapes nosaukums atbilst namespace)
namespace App\Controller;

use flight\Engine;

class MyController
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function index(): void
    {
        // Nav Flight:: fasādes lietojumprogrammas slānī — vieglāk testēt un skaidrāk AI rīkiem
        $this->app->render('welcome', ['message' => 'Hello']);
    }
}
// app/config/routes.php
use App\Controller\MyController;

$router->get('/', [MyController::class, 'index']);

Ja izlaižat Engine aizstāšanu, Dice var izveidot otru Engine, un jūsu kontrolleris nedalīs maršrutus, konfigurāciju vai kartēto Twig render no sāknēšanas faila.

Citu koplietotu pakalpojumu pievienošana (SimplePdo, Config, Twig)

use flight\database\SimplePdo;
use flight\Engine;

// Pēc tam, kad services.php izveidojat $db, $config, $twig:
$substitutions = [
    Engine::class => $app,
    SimplePdo::class => $db,
    // App\Utils\Config::class => $config,
    // \Twig\Environment::class => $twig,
];

$container = $container->addRule('*', [
    'substitutions' => $substitutions,
]);

Tad kontrolleri var konstruktorā pieņemt SimplePdo $db (vai jūsu config tipu) un nekad neizsaukt Flight::db(). Tas atbilst vienību testēšanas norādēm un skeleton stila paraugam.

Citu klašu pievienošana

Ja jums ir citas klases, kuras vēlaties pievienot konteineram, ar Dice tas ir vienkārši, jo konteiners tās atrisinās automātiski. Šeit ir piemērs:


$container = new \Dice\Dice;
// Ja jums nav nepieciešams ievadīt atkarības savās klasēs,
// jums nekas nav jādefinē!
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

class MyCustomClass {
    public function parseThing() {
        return 'thing';
    }
}

class UserController {

    protected MyCustomClass $MyCustomClass;

    public function __construct(MyCustomClass $MyCustomClass) {
        $this->MyCustomClass = $MyCustomClass;
    }

    public function index() {
        echo $this->MyCustomClass->parseThing();
    }
}

Flight::route('/user', 'UserController->index');

PSR-11

Flight var izmantot arī jebkuru PSR-11 saderīgu konteineru. Tas nozīmē, ka varat izmantot jebkuru konteineru, kas implementē PSR-11 saskarni. Šeit ir piemērs, izmantojot League PSR-11 konteineru:


require 'vendor/autoload.php';

use flight\database\SimplePdo;

// tā pati UserController ideja kā iepriekš, bet ar SimplePdo tipu, nevis neapstrādātu PDO

$container = new \League\Container\Container();
$container->add(UserController::class)->addArgument(SimplePdo::class);
$container->add(SimplePdo::class)
    ->addArgument('mysql:host=localhost;dbname=test')
    ->addArgument('user')
    ->addArgument('pass');
Flight::registerContainerHandler($container);

Flight::route('/user', [ 'UserController', 'view' ]);

Flight::start();

Tas var būt nedaudz garāks nekā iepriekšējais Dice piemērs, taču tas joprojām sasniedz to pašu rezultātu ar tādiem pašiem ieguvumiem!

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/middleware

Starpamats

Pārskats

Flight atbalsta maršruta un grupas maršruta vidusprogrammatūru. Vidusprogrammatūra ir jūsu lietojumprogrammas daļa, kur kods tiek izpildīts pirms ( vai pēc) maršruta atsauksmes. Tas ir lielisks veids, kā pievienot API autentifikācijas pārbaudes jūsu kodā vai lai pārbaudītu, vai lietotājam ir atļauja piekļūt maršrutam.

Saprašana

Vidusprogrammatūra var ievērojami vienkāršot jūsu lietojumprogrammu. Tā vietā, lai izmantotu sarežģītu abstraktas klases mantojumu vai metožu pārrakstīšanu, vidusprogrammatūra ļauj jums kontrolēt savus maršrutus, pievienojot tiem savu pielāgotu lietojumprogrammas loģiku. Jūs varat domāt par vidusprogrammatūru gandrīz kā par sendviču. Jums ir maize no ārpuses, un tad slāņi ar sastāvdaļām, piemēram, salātiem, tomātiem, gaļām un sieru. Tad iedomājieties, ka katrs pieprasījums ir kā iekost sendvičā, kur jūs ēdat ārējos slāņus vispirms un pakāpeniski nokļūstat līdz kodolam.

Šeit ir vizuāls attēlojums, kā darbojas vidusprogrammatūra. Tad mēs parādīsim jums praktisku piemēru, kā tas darbojas.

Lietotāja pieprasījums URL /api ----> 
    Vidusprogrammatūra->before() izpildīta ----->
        Atsaucamā funkcija/metode, kas pievienota /api, izpildīta un atbilde ģenerēta ------>
    Vidusprogrammatūra->after() izpildīta ----->
Lietotājs saņem atbildi no servera

Un šeit ir praktisks piemērs:

Lietotājs pāriet uz URL /dashboard
    LoggedInMiddleware->before() izpildīta
        before() pārbauda derīgu pieteikšanās sesiju
            ja jā, nedarīt neko un turpināt izpildi
            ja nē, novirzīt lietotāju uz /login
                Atsaucamā funkcija/metode, kas pievienota /api, izpildīta un atbilde ģenerēta
    LoggedInMiddleware->after() neko nav definēts, tāpēc ļauj izpildei turpināties
Lietotājs saņem dashboard HTML no servera

Izpildes secība

Vidusprogrammatūras funkcijas tiek izpildītas tajā secībā, kā tās tiek pievienotas maršrutam. Izpilde ir līdzīga tam, kā Slim Framework apstrādā šo.

before() metodes tiek izpildītas pievienotās secībā, un after() metodes tiek izpildītas pretējā secībā.

Piem.: Middleware1->before(), Middleware2->before(), Middleware2->after(), Middleware1->after().

Pamata izmantošana

Jūs varat izmantot vidusprogrammatūru kā jebkuru atsaucamu metodi, tostarp anonīmu funkciju vai klasi (ieteicams)

Anonīma funkcija

Šeit ir vienkāršs piemērs:

Flight::route('/path', function() { echo ' Here I am!'; })->addMiddleware(function() {
    echo 'Middleware first!';
});

Flight::start();

// This will output "Middleware first! Here I am!"

Piezīme: Izmantojot anonīmu funkciju, vienīgā interpretētā metode ir before() metode. Jūs nevarat definēt after() uzvedību ar anonīmu klasi.

Izmantojot klases

Vidusprogrammatūru var (un vajadzētu) reģistrēt kā klasi. Ja jums vajadzīga "after" funkcionalitāte, jūs jāizmanto klase.

class MyMiddleware {
    public function before($params) {
        echo 'Middleware first!';
    }

    public function after($params) {
        echo 'Middleware last!';
    }
}

$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware($MyMiddleware); 
// also ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);

Flight::start();

// This will display "Middleware first! Here I am! Middleware last!"

Jūs varat definēt tikai vidusprogrammatūras klases nosaukumu, un tā uzreiz radīs klases экземпlāru.

Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware(MyMiddleware::class); 

Piezīme: Ja jūs nodod tikai vidusprogrammatūras nosaukumu, tā automātiski tiks izpildīta, izmantojot atkarību injekcijas konteineru, un vidusprogrammatūra tiks izpildīta ar parametriem, kas tai nepieciešami. Ja jums nav reģistrēts atkarību injekcijas konteiners, tas pēc noklusējuma nodos flight\Engine экземпlāru __construct(Engine $app) metodē.

Izmantojot maršrutus ar parametriem

Ja jums vajadzīgi parametri no jūsu maršruta, tie tiks nodoti kā viens masīvs jūsu vidusprogrammatūras funkcijā. (function($params) { ... } vai public function before($params) { ... }). Iemesls tam ir tas, ka jūs varat strukturēt savus parametrus grupās, un dažās no tām jūsu parametri var parādīties citā secībā, kas salauztu vidusprogrammatūras funkciju, atsaucoties uz nepareizo parametru. Šādā veidā jūs varat piekļūt tiem pēc nosaukuma, nevis pozīcijas.

use flight\Engine;

class RouteSecurityMiddleware {

    protected Engine $app;

    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function before(array $params) {
        $clientId = $params['clientId'];

        // jobId may or may not be passed in
        $jobId = $params['jobId'] ?? 0;

        // maybe if there's no job ID, you don't need to lookup anything.
        if($jobId === 0) {
            return;
        }

        // perform a lookup of some kind in your database
        $isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);

        if($isValid !== true) {
            $this->app->halt(400, 'You are blocked, muahahaha!');
        }
    }
}

// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {

    // This group below still gets the parent middleware
    // But the parameters are passed in one single array 
    // in the middleware.
    $router->group('/job/@jobId', function(Router $router) {
        $router->get('', [ JobController::class, 'view' ]);
        $router->put('', [ JobController::class, 'update' ]);
        $router->delete('', [ JobController::class, 'delete' ]);
        // more routes...
    });
}, [ RouteSecurityMiddleware::class ]);

Grupēšana maršrutus ar vidusprogrammatūru

Jūs varat pievienot maršruta grupu, un tad katrs maršruts šajā grupā būs ar to pašu vidusprogrammatūru. Tas ir noderīgi, ja jums vajag grupēt vairākus maršrutus, piemēram, ar Auth vidusprogrammatūru, lai pārbaudītu API atslēgu galvenē.


// added at the end of the group method
Flight::group('/api', function() {

    // This "empty" looking route will actually match /api
    Flight::route('', function() { echo 'api'; }, false, 'api');
    // This will match /api/users
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    // This will match /api/users/1234
    Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);

Ja jūs vēlaties piemērot globālu vidusprogrammatūru visiem jūsu maršrutiem, jūs varat pievienot "tukšu" grupu:


// added at the end of the group method
Flight::group('', function() {

    // This is still /users
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    // And this is still /users/1234
    Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ ApiAuthMiddleware::class ]); // or [ new ApiAuthMiddleware() ], same thing

Izplatīti izmantošanas gadījumi

API atslēgas validācija

Ja jūs vēlaties aizsargāt savus /api maršrutus, pārbaudot, vai API atslēga ir pareiza, jūs varat viegli to apstrādāt ar vidusprogrammatūru.

use flight\Engine;

class ApiMiddleware {

    protected Engine $app;

    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function before(array $params) {
        $authorizationHeader = $this->app->request()->getHeader('Authorization');
        $apiKey = str_replace('Bearer ', '', $authorizationHeader);

        // do a lookup in your database for the api key
        $apiKeyHash = hash('sha256', $apiKey);
        $hasValidApiKey = !!$this->db()->fetchField("SELECT 1 FROM api_keys WHERE hash = ? AND valid_date >= NOW()", [ $apiKeyHash ]);

        if($hasValidApiKey !== true) {
            $this->app->jsonHalt(['error' => 'Invalid API Key']);
        }
    }
}

// routes.php
$router->group('/api', function(Router $router) {
    $router->get('/users', [ ApiController::class, 'getUsers' ]);
    $router->get('/companies', [ ApiController::class, 'getCompanies' ]);
    // more routes...
}, [ ApiMiddleware::class ]);

Tagad visi jūsu API maršruti ir aizsargāti ar šo API atslēgas validācijas vidusprogrammatūru, ko jūs esat iestatījis! Ja jūs pievienosiet vairāk maršrutus maršrutētāja grupai, tiem uzreiz būs tā pati aizsardzība!

Pieteikšanās validācija

Vai jūs vēlaties aizsargāt dažus maršrutus, lai tie būtu pieejami tikai pieteikušies lietotājiem? To var viegli sasniegt ar vidusprogrammatūru!

use flight\Engine;

class LoggedInMiddleware {

    protected Engine $app;

    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function before(array $params) {
        $session = $this->app->session();
        if($session->get('logged_in') !== true) {
            $this->app->redirect('/login');
            exit;
        }
    }
}

// routes.php
$router->group('/admin', function(Router $router) {
    $router->get('/dashboard', [ DashboardController::class, 'index' ]);
    $router->get('/clients', [ ClientController::class, 'index' ]);
    // more routes...
}, [ LoggedInMiddleware::class ]);

Maršruta parametra validācija

Vai jūs vēlaties aizsargāt savus lietotājus no vērtību maiņas URL, lai piekļūtu datiem, kas tiem nepieder? To var atrisināt ar vidusprogrammatūru!

use flight\Engine;

class RouteSecurityMiddleware {

    protected Engine $app;

    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function before(array $params) {
        $clientId = $params['clientId'];
        $jobId = $params['jobId'];

        // perform a lookup of some kind in your database
        $isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);

        if($isValid !== true) {
            $this->app->halt(400, 'You are blocked, muahahaha!');
        }
    }
}

// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {
    $router->get('', [ JobController::class, 'view' ]);
    $router->put('', [ JobController::class, 'update' ]);
    $router->delete('', [ JobController::class, 'delete' ]);
    // more routes...
}, [ RouteSecurityMiddleware::class ]);

Apstrāde vidusprogrammatūras izpildes

Pieņemsim, jums ir autentifikācijas vidusprogrammatūra, un jūs vēlaties novirzīt lietotāju uz pieteikšanās lapu, ja viņš nav autentificēts. Jums ir vairākas opcijas rīcībā:

  1. Jūs varat atgriezt false no vidusprogrammatūras funkcijas, un Flight automātiski atgriezīs 403 Forbidden kļūdu, bet bez pielāgošanas.
  2. Jūs varat novirzīt lietotāju uz pieteikšanās lapu, izmantojot Flight::redirect().
  3. Jūs varat izveidot pielāgotu kļūdu vidusprogrammatūrā un apturēt maršruta izpildi.

Vienkāršs un tiešs

Šeit ir vienkāršs return false; piemērs:

class MyMiddleware {
    public function before($params) {
        $hasUserKey = Flight::session()->exists('user');
        if ($hasUserKey === false) {
            return false;
        }

        // since it's true, everything just keeps on going
    }
}

Novirzīšanas piemērs

Šeit ir piemērs, kā novirzīt lietotāju uz pieteikšanās lapu:

class MyMiddleware {
    public function before($params) {
        $hasUserKey = Flight::session()->exists('user');
        if ($hasUserKey === false) {
            Flight::redirect('/login');
            exit;
        }
    }
}

Pielāgota kļūdas piemērs

Pieņemsim, jums vajag mest JSON kļūdu, jo jūs veidojat API. Jūs varat to izdarīt šādi:

class MyMiddleware {
    public function before($params) {
        $authorization = Flight::request()->getHeader('Authorization');
        if(empty($authorization)) {
            Flight::jsonHalt(['error' => 'You must be logged in to access this page.'], 403);
            // or
            Flight::json(['error' => 'You must be logged in to access this page.'], 403);
            exit;
            // or
            Flight::halt(403, json_encode(['error' => 'You must be logged in to access this page.']);
        }
    }
}

Skatīt arī

Traucējummeklēšana

Izmaiņu žurnāls

Learn/filtering

Filtrēšana

Pārskats

Flight ļauj jums filtrēt kartētos metodes pirms un pēc to izsaukšanas.

Saprašana

Nav iepriekš definētu āķu, kurus jums vajadzētu iegaumēt. Jūs varat filtrēt jebkuru no noklusējuma ietvara metodēm, kā arī jebkuru pielāgotu metožu, kuras esat kartējuši.

Filtra funkcija izskatās šādi:

/**
 * @param array $params Metodei, kas tiek filtrēta, nodotie parametri.
 * @param string $output (tikai v2 izvades buferizēšana) Metodes, kas tiek filtrēta, izvade.
 * @return bool Atgrieziet true/void vai neatgrieziet, lai turpinātu ķēdi, false, lai pārtrauktu ķēdi.
 */
function (array &$params, string &$output): bool {
  // Filtra kods
}

Izmantojot nodotās mainīgās, jūs varat manipulēt ar ievades parametriem un/vai izvadi.

Jūs varat likt filtram darboties pirms metodes, izdarot:

Flight::before('start', function (array &$params, string &$output): bool {
  // Dariet kaut ko
});

Jūs varat likt filtram darboties pēc metodes, izdarot:

Flight::after('start', function (array &$params, string &$output): bool {
  // Dariet kaut ko
});

Jūs varat pievienot tik daudz filtru, cik vēlaties, jebkurai metodei. Tie tiks izsaukti secībā, kādā tie ir deklarēti.

Šeit ir filtrēšanas procesa piemērs:

// Kartējiet pielāgotu metodi
Flight::map('hello', function (string $name) {
  return "Hello, $name!";
});

// Pievienojiet pirms filtra
Flight::before('hello', function (array &$params, string &$output): bool {
  // Manipulējiet parametru
  $params[0] = 'Fred';
  return true;
});

// Pievienojiet pēc filtra
Flight::after('hello', function (array &$params, string &$output): bool {
  // Manipulējiet izvadi
  $output .= " Have a nice day!";
  return true;
});

// Izsauciet pielāgoto metodi
echo Flight::hello('Bob');

Šim vajadzētu parādīt:

Hello Fred! Have a nice day!

Ja esat definējuši vairākus filtrus, jūs varat pārtraukt ķēdi, atgriežot false jebkurā no jūsu filtra funkcijām:

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'one';
  return true;
});

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'two';

  // Tas beigs ķēdi
  return false;
});

// Tas netiks izsaukts
Flight::before('start', function (array &$params, string &$output): bool {
  echo 'three';
  return true;
});

Piezīme: Kodola metodes, piemēram, map un register, nevar tikt filtrētas, jo tās tiek izsauktas tieši un ne dinamiski. Skatiet Extending Flight, lai iegūtu vairāk informācijas.

Skatīt arī

Traucējummeklēšana

Izmaiņu žurnāls

Learn/requests

Pieprasījumi

Pārskats

Flight ieslēdz HTTP pieprasījumu vienā objektā, ko var piekļūt, veicot:

$request = Flight::request();

Saprašana

HTTP pieprasījumi ir viens no galvenajiem HTTP dzīves cikla aspektiem, ko jāizprot. Lietotājs veic darbību tīmekļa pārlūkprogrammā vai HTTP klientā, un tie nosūta virkni galvenes, ķermeņa, URL utt. uz jūsu projektu. Jūs varat uztvert šīs galvenes (pārlūkprogrammas valoda, kādu kompresijas veidu tās var apstrādāt, lietotāja aģents utt.) un uztvert ķermeni un URL, kas nosūtīts uz jūsu Flight lietojumprogrammu. Šie pieprasījumi ir būtiski, lai jūsu lietojumprogramma saprastu, ko darīt tālāk.

Pamata Izmantošana

PHP ir vairākas superglobālās mainīgās, tostarp $_GET, $_POST, $_REQUEST, $_SERVER, $_FILES un $_COOKIE. Flight abstraktē šīs prom praktiskajās Kolekcijās. Jūs varat piekļūt query, data, cookies un files īpašībām kā masīviem vai objektiem.

Piezīme: Ir ĻOTI atgrūstami izmantot šīs superglobālās mainīgās savā projektā, un tām jāatsaucas caur request() objektu.

Piezīme: Nav pieejama abstrakcija $_ENV.

$_GET

Jūs varat piekļūt $_GET masīvam caur query īpašību:

// GET /search?keyword=something
Flight::route('/search', function(){
    $keyword = Flight::request()->query['keyword'];
    // or
    $keyword = Flight::request()->query->keyword;
    echo "You are searching for: $keyword";
    // query a database or something else with the $keyword
});

$_POST

Jūs varat piekļūt $_POST masīvam caur data īpašību:

Flight::route('POST /submit', function(){
    $name = Flight::request()->data['name'];
    $email = Flight::request()->data['email'];
    // or
    $name = Flight::request()->data->name;
    $email = Flight::request()->data->email;
    echo "You submitted: $name, $email";
    // save to a database or something else with the $name and $email
});

$_COOKIE

Jūs varat piekļūt $_COOKIE masīvam caur cookies īpašību:

Flight::route('GET /login', function(){
    $savedLogin = Flight::request()->cookies['myLoginCookie'];
    // or
    $savedLogin = Flight::request()->cookies->myLoginCookie;
    // check if it's really saved or not and if it is auto log them in
    if($savedLogin) {
        Flight::redirect('/dashboard');
        return;
    }
});

Lai iegūtu palīdzību par jaunu sīkfailu vērtību iestatīšanu, skatiet overclokk/cookie

$_SERVER

Ir pieejams saīsinājums, lai piekļūtu $_SERVER masīvam caur getVar() metodi:


$host = Flight::request()->getVar('HTTP_HOST');

$_FILES

Jūs varat piekļūt augšupielādētajām failiem caur files īpašību:

// raw access to $_FILES property. See below for recommended approach
$uploadedFile = Flight::request()->files['myFile']; 
// or
$uploadedFile = Flight::request()->files->myFile;

Skatiet Uploaded File Handler vairāk informācijas.

Failu Augšupielādes Apstrāde

v3.12.0

Jūs varat apstrādāt failu augšupielādi, izmantojot ietvaru ar dažām palīgapstrādes metodēm. Tas būtībā samazinās līdz faila datu iegūšanai no pieprasījuma un to pārvietošanai uz jaunu atrašanās vietu.

Flight::route('POST /upload', function(){
    // If you had an input field like <input type="file" name="myFile">
    $uploadedFileData = Flight::request()->getUploadedFiles();
    $uploadedFile = $uploadedFileData['myFile'];
    $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});

Ja jums ir vairāki augšupielādēti faili, jūs varat tos iziet cauri cilpai:

Flight::route('POST /upload', function(){
    // If you had an input field like <input type="file" name="myFiles[]">
    $uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
    foreach ($uploadedFiles as $uploadedFile) {
        $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
    }
});

Drošības Piezīme: Vienmēr validējiet un sanitizējiet lietotāja ievadi, īpaši, kad strādājat ar failu augšupielādi. Vienmēr validējiet paplašinājumu veidus, kurus atļausiet augšupielādēt, bet jums arī vajadzētu validēt faila "magic bytes", lai nodrošinātu, ka tas patiešām ir tāds faila veids, kādu lietotājs apgalvo. Ir pieejami raksti un bibliotēkas, lai palīdzētu ar to.

Pieprasījuma Ķermenis

Lai iegūtu neapstrādātu HTTP pieprasījuma ķermeni, piemēram, strādājot ar POST/PUT pieprasījumiem, jūs varat veikt:

Flight::route('POST /users/xml', function(){
    $xmlBody = Flight::request()->getBody();
    // do something with the XML that was sent.
});

JSON Ķermenis

Ja jūs saņemat pieprasījumu ar satura veidu application/json un piemēra datiem {"id": 123} tas būs pieejams no data īpašības:

$id = Flight::request()->data->id;

Pieprasījuma Galvenes

Jūs varat piekļūt pieprasījuma galvenēm, izmantojot getHeader() vai getHeaders() metodi:


// Maybe you need Authorization header
$host = Flight::request()->getHeader('Authorization');
// or
$host = Flight::request()->header('Authorization');

// If you need to grab all headers
$headers = Flight::request()->getHeaders();
// or
$headers = Flight::request()->headers();

Pieprasījuma Metode

Jūs varat piekļūt pieprasījuma metodei, izmantojot method īpašību vai getMethod() metodi:

$method = Flight::request()->method; // actually populated by getMethod()
$method = Flight::request()->getMethod();

Piezīme: getMethod() metode vispirms iegūst metodi no $_SERVER['REQUEST_METHOD'], tad to var pārspēt $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'], ja tā pastāv, vai $_REQUEST['_method'], ja tā pastāv.

Pieprasījuma Objekta Īpašības

Pieprasījuma objekts nodrošina šādas īpašības:

Palīgapstrādes Metodes

Ir dažas palīgapstrādes metodes, lai saliktu URL daļas vai strādātu ar noteiktām galvenēm.

Pilns URL

Jūs varat piekļūt pilnam pieprasījuma URL, izmantojot getFullUrl() metodi:

$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar

Bāzes URL

Jūs varat piekļūt bāzes URL, izmantojot getBaseUrl() metodi:

// http://example.com/path/to/something/cool?query=yes+thanks
$url = Flight::request()->getBaseUrl();
// https://example.com
// Notice, no trailing slash.

Vaicājuma Parsēšana

Jūs varat nodot URL uz parseQuery() metodi, lai parsētu vaicājuma virkni asociatīvā masīvā:

$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']

Saturu Pieņemšanas Veidu Sarunas

v3.17.2

Jūs varat izmantot negotiateContentType() metodi, lai noteiktu labāko satura veidu, ar kuru atbildēt, balstoties uz Accept galveni, ko nosūtījis klients.


// Example Accept header: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
// The below defines what you support.
$availableTypes = ['application/json', 'application/xml'];
$typeToServe = Flight::request()->negotiateContentType($availableTypes);
if ($typeToServe === 'application/json') {
    // Serve JSON response
} elseif ($typeToServe === 'application/xml') {
    // Serve XML response
} else {
    // Default to something else or throw an error
}

Piezīme: Ja neviena no pieejamajām veidiem nav atrasta Accept galvenē, metode atgriezīs null. Ja nav definēta Accept galvene, metode atgriezīs pirmo veidu $availableTypes masīvā.

Skatīt Arī

Traucējummeklēšana

Izmaiņu Žurnāls

Learn/why_frameworks

Kāpēc ietvaru?

Daudzi programmētāji stingri iebilst pret ietvaru izmantošanu. Viņi argumentē, ka ietvari ir pārpildīti, lēni un grūti apgūstami. Viņi saka, ka ietvari nav nepieciešami un ka jūs varat rakstīt labāku kodu bez tiem. Noteikti var izteikt dažus pamatoti iebildumus pret ietvaru izmantošanu. Tomēr ir arī daudz priekšrocību, izmantojot ietvarus.

Iemesli izmantot ietvaru

Šeit ir daži iemesli, kāpēc jums varētu būt jāapsver ietvara izmantošana:

Flight ir mikroietvars. Tas nozīmē, ka tas ir neliels un viegls. Tas nepiedāvā tik daudz funkcionalitātes kā lielāki ietvari, piemēram, Laravel vai Symfony. Tomēr tas nodrošina lielu daļu funkcionalitātes, kas jums nepieciešama, lai veidotu tīmekļa lietojumprogrammas. Tas arī ir viegli apgūstams un lietojams. Tas padara to par labu izvēli, lai ātri un viegli veidotu tīmekļa lietojumprogrammas. Ja esat jauns ietvaru jomā, Flight ir lielisks iesācēju ietvars, ar kuru sākt. Tas palīdzēs jums iemācīties par ietvaru izmantošanas priekšrocībām, neplūstot jums ar pārāk daudz sarežģītības. Kad esat ieguvis kādu pieredzi ar Flight, būs vieglāk pāriet uz sarežģītākiem ietvariem, piemēram, Laravel vai Symfony, taču Flight joprojām var veiksmīgi veidot izturīgu lietojumprogrammu.

Kas ir Maršrutēšana?

Maršrutēšana ir Flight ietvara pamatā, bet kas tas īsti ir? Maršrutēšana ir process, kurā tiek ņemts URL un tiek saskaņots ar konkrētu funkciju jūsu kodā. Tā ir veids, kā padarīt jūsu tīmekļa vietni dažāda satura, balstoties uz pieprasīto URL. Piemēram, jūs varētu vēlēties parādīt lietotāja profilu, kad viņi apmeklē /lietotājs/1234, bet parādīt visu lietotāju sarakstu, kad viņi apmeklē /lietotāji. Tas ir visu izdarīts caur maršrutēšanu.

Tas varētu darboties šādi:

Un kāpēc ir svarīgi?

Pareiza centralizēta maršrutētāja sistēma faktiski var padarīt jūsu dzīvi dramatiski vieglāku! Tas var būt grūti pamanāms sākumā. Šeit ir daži iemesli, kāpēc:

Es esmu drošs, ka esat iepazinies ar skriptu pa skriptam veidu, kā radīt tīmekļa vietni. Jums var būt fails, ko sauc index.php, kurā ir daudz if paziņojumu, lai pārbaudītu URL un tad izpildītu konkrētu funkciju, pamatojoties uz URL. Tas ir veids, kā maršrutēšana, bet tas nav ļoti organizēts un tas var ātri izvērsties. Flight maršrutēšanas sistēma ir daudz organizētāka un spēcīgāka veidā, kā apstrādāt maršrutēšanu.

Tas?

// /lietotajs/skatit_profila.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    skatitLietotajaProfilu($id);
}

// /lietotajs/rediget_profila.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    reditLietotajaProfilu($id);
}

// u.c...

Vai tas?

// index.php
Flight::route('/lietotajs/@id', ['LietotajaKontrolieris', 'skatitLietotajaProfilu']);
Flight::route('/lietotajs/@id/edit', ['LietotajaKontrolieris', 'reditLietotajaProfilu']);

// Varbūt jūsu app/controllers/LietotajaKontrolieris.php
class LietotajaKontrolieris {
    public function skatitLietotajaProfilu($id) {
        // darīt kaut ko
    }

    public function reditLietotajaProfilu($id) {
        // darīt kaut ko
    }
}

Cerams, jūs sākat ieraudzīt ieguvumus no centralizētas maršrutēšanas sistēmas izmantošanas. Ilgtermiņā to ir daudz vieglāk pārvaldīt un saprast!

Pieprasījumi un Atbildes

Flight nodrošina vienkāršu un vieglu veidu, kā apstrādāt pieprasījumus un atbildes. Tas ir tīmekļa ietvaru būtība. Tas pieņem pieprasījumu no lietotāja pārlūka, apstrādā to, un pēc tam nosūta atbildi. Tā ir veids, kā jūs varat veidot tīmekļa lietojumprogrammas, kas darbojas, piemēram, rāda lietotāja profilu, ļauj lietotājam pierakstīties vai ļauj lietotājam publicēt jaunu bloga ierakstu.

Pieprasījumi

Pieprasījums ir tas, ko lietotāja pārlūks sūta uz jūsu serveri, kad viņi apmeklē jūsu tīmekļa vietni. Šis pieprasījums satur informāciju par to, ko lietotājs vēlas darīt. Piemēram, tas var saturēt informāciju par to, kādu URL lietotājs vēlas apmeklēt, kādu datu lietotājs vēlas nosūtīt uz jūsu serveri vai kādu veidu dati lietotājs vēlas saņemt no jūsu servera. Svarīgi ir zināt, ka pieprasījums ir tikai lasīšanas režīmā. Jūs nevarat mainīt pieprasījumu, bet jūs to varat lasīt.

Flight nodrošina vienkāršu veidu, kā piekļūt informācijai par pieprasījumu. Jūs varat piekļūt informācijai par pieprasījumu, izmantojot Flight::request() metodi. Šī metode atgriež Pieprasījums objektu, kas satur informāciju par pieprasījumu. Jūs varat izmantot šo objektu, lai piekļūtu informācijai par pieprasījumu, piemēram, URL, metodi vai datiem, ko lietotājs nosūtījis uz jūsu serveri.

Atbildes

Atbilde ir tas, ko jūsu serveris nosūta atpakaļ uz lietotāja pārlūku, kad viņi apmeklē jūsu tīmekļa vietni. Šī atbilde satur informāciju par to, ko jūsu serveris vēlas darīt. Piemēram, tas var saturēt informāciju par to, kāda veida datus jūsu serveris vēlas nosūtīt lietotājam, kāda veida datus jūsu serveris vēlas saņemt no lietotāja vai kāda veida datus jūsu serveris vēlas saglabāt lietotāja datorā.

Flight nodrošina vienkāršu veidu, kā nosūtīt atbildi uz lietotāja pārlūku. Jūs varat nosūtīt atbildi, izmantojot Flight::response() metodi. Šī metode nospiež Atbilde objektu kā argumentu un nosūta atbildi uz lietotāja pārlūku. Jūs varat izmantot šo objektu, lai nosūtītu atbildi uz lietotāja pārlūku, piemēram, HTML, JSON vai failu. Flight palīdz jums automātiski ģenerēt dažas atbildes daļas, lai padarītu lietas vieglas,# Kāpēc ietvaru?

Daudzi programmētāji stingri iebilst pret ietvaru izmantošanu. Viņi argumentē, ka ietvari ir pārpildīti, lēni un grūti apgūstami. Viņi saka, ka ietvari nav nepieciešami un ka jūs varat rakstīt labāku kodu bez tiem. Noteikti var izteikt dažus pamatoti iebildumus pret ietvaru izmantošanu. Tomēr ir arī daudz priekšrocību, izmantojot ietvarus.

Iemesli izmantot ietvaru

Šeit ir daži iemesli, kāpēc jums varētu būt jāapsver ietvara izmantošana:

Flight ir mikroietvars. Tas nozīmē, ka tas ir neliels un viegls. Tas nepiedāvā tik daudz funkcionalitātes kā lielāki ietvari, piemēram, Laravel vai Symfony. Tomēr tas nodrošina lielu daļu funkcionalitātes, kas jums nepieciešama, lai veidotu tīmekļa lietojumprogrammas. Tas arī ir viegli apgūstams un lietojams. Tas padara to par labu izvēli, lai ātri un viegli veidotu tīmekļa lietojumprogrammas. Ja esat jauns ietvaru jomā, Flight ir lielisks iesācēju ietvars, ar kuru sākt. Tas palīdzēs jums iemācīties par ietvaru izmantošanas priekšrocībām, neplūstot jums ar pārāk daudz sarežģītības. Kad esat ieguvis kādu pieredzi...

Learn/responses

Atbildes

Pārskats

Flight palīdz ģenerēt daļu no atbildes galvenes jums, bet jūs kontrolējat lielāko daļu no tā, ko nosūtāt atpakaļ lietotājam. Lielāko daļu laika jūs tieši piekļūsiet response() objektam, bet Flight piedāvā dažas palīgmēģinājumu metodes, lai iestatītu dažas atbildes galvenes jums.

Izpratne

Pēc tam, kad lietotājs nosūta savu pieprasījumu uz jūsu lietojumprogrammu, jums jāģenerē pareiza atbilde viņiem. Viņi ir nosūtījuši jums informāciju, piemēram, valodu, kuru viņi dod priekšroku, vai viņi var apstrādāt noteiktus kompresijas veidus, viņu lietotāja aģentu utt., un pēc visu apstrādes ir pienācis laiks nosūtīt viņiem atpakaļ pareizu atbildi. Tas var būt galvenes iestatīšana, HTML vai JSON ķermeņa izvade viņiem vai novirzīšana uz lapu.

Pamata lietošana

Atbildes ķermeņa nosūtīšana

Flight izmanto ob_start(), lai buferētu izvadi. Tas nozīmē, ka jūs varat izmantot echo vai print, lai nosūtītu atbildi lietotājam, un Flight to uztvers un nosūtīs atpakaļ lietotājam ar atbilstošajām galvenēm.

// Tas nosūtīs "Hello, World!" uz lietotāja pārlūkprogrammu
Flight::route('/', function() {
    echo "Hello, World!";
});

// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!

Kā alternatīvu, jūs varat izsaukt write() metodi, lai pievienotu ķermenim.

// Tas nosūtīs "Hello, World!" uz lietotāja pārlūkprogrammu
Flight::route('/', function() {
    // verbose, bet dažreiz tas ir nepieciešams
    Flight::response()->write("Hello, World!");

    // ja vēlaties iegūt ķermeni, kuru esat iestatījis šajā brīdī
    // jūs varat to izdarīt šādi
    $body = Flight::response()->getBody();
});

JSON

Flight nodrošina atbalstu JSON un JSONP atbilžu nosūtīšanai. Lai nosūtītu JSON atbildi, jūs nododiet datus, kas jākodē JSON:

Flight::route('/@companyId/users', function(int $companyId) {
    // kaut kā izvilkt savus lietotājus no datubāzes, piemēram
    $users = Flight::db()->fetchAll("SELECT id, first_name, last_name FROM users WHERE company_id = ?", [ $companyId ]);

    Flight::json($users);
});
// [{"id":1,"first_name":"Bob","last_name":"Jones"}, /* more users */ ]

Piezīme: Pēc noklusējuma Flight nosūtīs Content-Type: application/json galveni ar atbildi. Tas arī izmantos karodziņus JSON_THROW_ON_ERROR un JSON_UNESCAPED_SLASHES, kodējot JSON.

JSON ar statusa kodu

Jūs varat arī nodot statusa kodu kā otro argumentu:

Flight::json(['id' => 123], 201);

JSON ar skaistu izdruku

Jūs varat arī nodot argumentu pēdējā pozīcijā, lai iespējotu skaistu drukāšanu:

Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);

JSON argumentu secības maiņa

Flight::json() ir ļoti vecs metode, bet Flight mērķis ir saglabāt atpakaļsaderību projektiem. Tas ir ļoti vienkārši, ja vēlaties pārkārtot argumentu secību, lai izmantotu vienkāršāku sintaksi, jūs varat tikai pārkartēt JSON metodi kā jebkuru citu Flight metodi:

Flight::map('json', function($data, $code = 200, $options = 0) {

    // tagad jums nav jāizmanto `true, 'utf-8'`, kad izmantojat json() metodi!
    Flight::_json($data, $code, true, 'utf-8', $options);
}

// Un tagad to var izmantot šādi
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);

JSON un izpildes apturēšana

v3.10.0

Ja vēlaties nosūtīt JSON atbildi un apturēt izpildi, jūs varat izmantot jsonHalt() metodi. Tas ir noderīgi gadījumos, kad jūs pārbaudāt, iespējams, kādu autorizācijas veidu, un ja lietotājs nav autorizēts, jūs varat nekavējoties nosūtīt JSON atbildi, notīrīt esošo ķermeņa saturu un apturēt izpildi.

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // Pārbaudiet, vai lietotājs ir autorizēts
    if($authorized === false) {
        Flight::jsonHalt(['error' => 'Unauthorized'], 401);
        // nav izvades; nepieciešams šeit.
    }

    // Turpiniet ar pārējo maršrutu
});

Pirms v3.10.0, jums būtu jādara kaut kas šāds:

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // Pārbaudiet, vai lietotājs ir autorizēts
    if($authorized === false) {
        Flight::halt(401, json_encode(['error' => 'Unauthorized']));
    }

    // Turpiniet ar pārējo maršrutu
});

Atbildes ķermeņa notīrīšana

Ja vēlaties notīrīt atbildes ķermeni, jūs varat izmantot clearBody metodi:

Flight::route('/', function() {
    if($someCondition) {
        Flight::response()->write("Hello, World!");
    } else {
        Flight::response()->clearBody();
    }
});

Iepriekš minētais lietošanas gadījums, iespējams, nav izplatīts, tomēr tas varētu būt izplatītāks, ja tas tiktu izmantots starpprogrammatūrā.

Izpildes palaišana uz atbildes ķermeņa

Jūs varat palaist izpildes funkciju uz atbildes ķermeņa, izmantojot addResponseBodyCallback metodi:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);
});

// Tas sasniegs visas atbildes visiem maršrutiem
Flight::response()->addResponseBodyCallback(function($body) {
    return gzencode($body, 9);
});

Jūs varat pievienot vairākas izpildes funkcijas, un tās tiks palaistas secībā, kādā tās tika pievienotas. Tā kā tas var pieņemt jebkuru izsaucamu, tas var pieņemt klases masīvu [ $class, 'method' ], aizvēršanu $strReplace = function($body) { str_replace('hi', 'there', $body); };, vai funkcijas nosaukumu 'minify', ja jums ir funkcija, lai samazinātu jūsu html kodu, piemēram.

Piezīme: Maršruta izpildes funkcijas nedarbosies, ja izmantojat flight.v2.output_buffering konfigurācijas opciju.

Īpašs maršruta izpildes funkcija

Ja vēlaties, lai tas attiektos tikai uz specifisku maršrutu, jūs varat pievienot izpildes funkciju pašā maršrutā:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);

    // Tas sasniegs tikai šo maršrutu atbildi
    Flight::response()->addResponseBodyCallback(function($body) {
        return gzencode($body, 9);
    });
});

Starpprogrammatūras opcija

Jūs varat arī izmantot starpprogrammatūru, lai piemērotu izpildes funkciju visiem maršrutiem caur starpprogrammatūru:

// MinifyMiddleware.php
class MinifyMiddleware {
    public function before() {
        // Šeit piemērojiet izpildes funkciju uz response() objektu.
        Flight::response()->addResponseBodyCallback(function($body) {
            return $this->minify($body);
        });
    }

    protected function minify(string $body): string {
        // kaut kā samaziniet ķermeni
        return $body;
    }
}

// index.php
Flight::group('/users', function() {
    Flight::route('', function() { /* ... */ });
    Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);

Statusa kodi

Jūs varat iestatīt atbildes statusa kodu, izmantojot status metodi:

Flight::route('/@id', function($id) {
    if($id == 123) {
        Flight::response()->status(200);
        echo "Hello, World!";
    } else {
        Flight::response()->status(403);
        echo "Forbidden";
    }
});

Ja vēlaties iegūt pašreizējo statusa kodu, jūs varat izmantot status metodi bez jebkādiem argumentiem:

Flight::response()->status(); // 200

Atbildes galvenes iestatīšana

Jūs varat iestatīt galveni, piemēram, atbildes satura veidu, izmantojot header metodi:

// Tas nosūtīs "Hello, World!" uz lietotāja pārlūkprogrammu kā vienkāršu tekstu
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    // vai
    Flight::response()->setHeader('Content-Type', 'text/plain');
    echo "Hello, World!";
});

Novirzīšana

Jūs varat novirzīt pašreizējo pieprasījumu, izmantojot redirect() metodi un nododot jaunu URL:

Flight::route('/login', function() {
    $username = Flight::request()->data->username;
    $password = Flight::request()->data->password;
    $passwordConfirm = Flight::request()->data->password_confirm;

    if($password !== $passwordConfirm) {
        Flight::redirect('/new/location');
        return; // tas ir nepieciešams, lai zemāk esošā funkcionalitāte neizpildītos
    }

    // pievienojiet jauno lietotāju...
    Flight::db()->runQuery("INSERT INTO users ....");
    Flight::redirect('/admin/dashboard');
});

Piezīme: Pēc noklusējuma Flight nosūta HTTP 303 ("See Other") statusa kodu. Jūs varat izvēles kārtā iestatīt pielāgotu kodu:

Flight::redirect('/new/location', 301); // pastāvīgs

Maršruta izpildes apturēšana

Jūs varat apturēt ietvaru un nekavējoties iziet jebkurā punktā, izsaucot halt metodi:

Flight::halt();

Jūs varat arī norādīt izvēles HTTP statusa kodu un ziņojumu:

Flight::halt(200, 'Be right back...');

Izsaucot halt, tas atmestīs jebkuru atbildes saturu līdz tam punktam un apturēs visu izpildi. Ja vēlaties apturēt ietvaru un izvadīt pašreizējo atbildi, izmantojiet stop metodi:

Flight::stop($httpStatusCode = null);

Piezīme: Flight::stop() ir dažas dīvainas uzvedības, piemēram, tas izvadīs atbildi, bet turpinās izpildīt jūsu skriptu, kas var nebūt tas, ko vēlaties. Jūs varat izmantot exit vai return pēc Flight::stop() izsaukšanas, lai novērstu turpmāku izpildi, bet parasti iesaka izmantot Flight::halt().

Tas saglabās galvenes atslēgu un vērtību atbildes objektā. Pieprasījuma dzīves cikla beigās tas izveidos galvenes un nosūtīs atbildi.

Uzlabota lietošana

Galvenes nosūtīšana nekavējoties

Var būt gadījumi, kad jums jāizdara kaut kas pielāgots ar galveni, un jums jānosūta galvene tajā pašā koda rindā, ar kuru strādājat. Ja jūs iestatāt straumētu maršrutu, tas ir tas, kas jums būtu nepieciešams. To var sasniegt caur response()->setRealHeader().

Flight::route('/', function() {
    Flight::response()->setRealHeader('Content-Type: text/plain');
    echo 'Streaming response...';
    sleep(5);
    echo 'Done!';
})->stream();

JSONP

JSONP pieprasījumiem jūs varat izvēles kārtā nodot vaicājuma parametra nosaukumu, ko izmantojat, lai definētu savu atgriezeniskās saites funkciju:

Flight::jsonp(['id' => 123], 'q');

Tātad, veicot GET pieprasījumu, izmantojot ?q=my_func, jums vajadzētu saņemt izvadi:

my_func({"id":123});

Ja nenododat vaicājuma parametra nosaukumu, tas pēc noklusējuma būs jsonp.

Piezīme: Ja joprojām izmantojat JSONP pieprasījumus 2025. gadā un vēlāk, ielēkiet čatā un pastāstiet mums, kāpēc! Mēs mīlam dzirdēt dažus labus kaujas/briesmu stāstus!

Atbildes datu notīrīšana

Jūs varat notīrīt atbildes ķermeni un galvenes, izmantojot clear() metodi. Tas notīrīs jebkuras galvenes, kas piešķirtas atbildei, notīrīs atbildes ķermeni un iestatīs statusa kodu uz 200.

Flight::response()->clear();

Tikai atbildes ķermeņa notīrīšana

Ja vēlaties notīrīt tikai atbildes ķermeni, jūs varat izmantot clearBody() metodi:

// Tas joprojām saglabās jebkuras galvenes, kas iestatītas uz response() objektu.
Flight::response()->clearBody();

HTTP kešošana

Flight nodrošina iebūvētu atbalstu HTTP līmeņa kešošanai. Ja kešošanas nosacījums ir izpildīts, Flight atgriezīs HTTP 304 Not Modified atbildi. Nākamreiz, kad klients pieprasa to pašu resursu, viņi tiks aicināti izmantot savu lokāli kešoto versiju.

Maršruta līmeņa kešošana

Ja vēlaties kešot visu savu atbildi, jūs varat izmantot cache() metodi un nodot kešošanas laiku.


// Tas kešos atbildi uz 5 minūtēm
Flight::route('/news', function () {
  Flight::response()->cache(time() + 300);
  echo 'This content will be cached.';
});

// Alternatīvi, jūs varat izmantot virkni, ko nodotu
// strtotime() metodei
Flight::route('/news', function () {
  Flight::response()->cache('+5 minutes');
  echo 'This content will be cached.';
});

Pēdējā modificēšana

Jūs varat izmantot lastModified metodi un nodot UNIX laika zīmogu, lai iestatītu datumu un laiku, kad lapa tika pēdējo reizi modificēta. Klients turpinās izmantot savu kešu, līdz pēdējās modificēšanas vērtība tiek mainīta.

Flight::route('/news', function () {
  Flight::lastModified(1234567890);
  echo 'This content will be cached.';
});

ETag

ETag kešošana ir līdzīga Last-Modified, izņemot to, ka jūs varat norādīt jebkuru ID, ko vēlaties resursam:

Flight::route('/news', function () {
  Flight::etag('my-unique-id');
  echo 'This content will be cached.';
});

Ņemiet vērā, ka izsaucot vai nu lastModified, vai etag, tas abus iestatīs un pārbaudīs keša vērtību. Ja keša vērtība ir tāda pati starp pieprasījumiem, Flight nekavējoties nosūtīs HTTP 304 atbildi un apturēs apstrādi.

Faila lejupielāde

v3.12.0

Ir palīgmēģinājuma metode, lai straumētu failu galapunktam. Jūs varat izmantot download metodi un nodot ceļu.

Flight::route('/download', function () {
  Flight::download('/path/to/file.txt');
  // No v3.17.1 jūs varat norādīt pielāgotu faila nosaukumu lejupielādei
  Flight::download('/path/to/file.txt', 'custom_name.txt');
});

Skatīt arī

Traucējummeklēšana

Izmaiņu žurnāls

Learn/events

Notikumu pārvaldnieks

no v3.15.0

Pārskats

Notikumi ļauj reģistrēt un izraisīt pielāgotu uzvedību jūsu lietojumprogrammā. Ar Flight::onEvent() un Flight::triggerEvent() pievienošanu jūs tagad varat savienoties ar galvenajiem jūsu lietojumprogrammas dzīves cikla brīžiem vai definēt savus notikumus (piemēram, paziņojumus un e-pastus), lai padarītu jūsu kodu modulārāku un paplašināmu. Šīs metodes ir daļa no Flight mappable methods, kas nozīmē, ka jūs varat pārdefinēt to uzvedību atbilstoši savām vajadzībām.

Saprašana

Notikumi ļauj atdalīt dažādas jūsu lietojumprogrammas daļas, lai tās pārāk stipri nepaļautos viena uz otru. Šī atdalīšana — bieži saukta par decoupling — padara jūsu kodu vieglāku atjaunināt, paplašināt vai atkļūdot. Tā vietā, lai rakstītu visu vienā lielā gabalā, jūs varat sadalīt savu loģiku mazākos, neatkarīgos gabalos, kas reaģē uz specifiskām darbībām (notikumiem).

Iedomājieties, ka jūs veidojat emuāru lietojumprogrammu:

Bez notikumiem jūs visu sapotu vienā funkcijā. Ar notikumiem jūs varat to sadalīt: viena daļa saglabā komentāru, cita izraisa notikumu, piemēram, 'comment.posted', un atsevišķi klausītāji apstrādā e-pastu un reģistrēšanu. Tas uztur jūsu kodu tīrāku un ļauj pievienot vai noņemt funkcijas (piemēram, paziņojumus) bez pieskāriena kodola loģikai.

Izplatīti izmantošanas gadījumi

Lielākoties notikumi ir piemēroti lietām, kas ir izvēles, bet ne absolūti kodola daļa jūsu sistēmā. Piemēram, sekojošie ir labi, bet ja tie kaut kāda iemesla dēļ neizdodas, jūsu lietojumprogramma joprojām darbojas:

Tomēr pieņemsim, ka jums ir aizmirsta parole funkcija. Tā jābūt jūsu kodola funkcionalitātes daļai un nevis notikumam, jo ja tas e-pasts netiek nosūtīts, jūsu lietotājs nevar atiestatīt paroli un izmantot jūsu lietojumprogrammu.

Pamata izmantošana

Flight notikumu sistēma ir balstīta uz divām galvenajām metodēm: Flight::onEvent() notikumu klausītāju reģistrēšanai un Flight::triggerEvent() notikumu izraisīšanai. Lūk, kā jūs varat tās izmantot:

Notikumu klausītāju reģistrēšana

Lai klausītos notikumu, izmantojiet Flight::onEvent(). Šī metode ļauj definēt, kas jānotiek, kad notikums notiek.

Flight::onEvent(string $event, callable $callback): void

Jūs "abonējat" notikumu, sakot Flight, ko darīt, kad tas notiek. Atsaukums var pieņemt argumentus, kas nodoti no notikuma izraisītāja.

Flight notikumu sistēma ir sinhrona, kas nozīmē, ka katrs notikuma klausītājs tiek izpildīts secīgi, viens pēc otra. Kad jūs izraisa notikumu, visi reģistrētie klausītāji tam notikumam tiks izpildīti līdz galam, pirms jūsu kods turpinās. Tas ir svarīgi saprast, jo tas atšķiras no asinhronām notikumu sistēmām, kur klausītāji var darboties paralēli vai vēlākā laikā.

Vienkāršs piemērs

Flight::onEvent('user.login', function ($username) {
    echo "Welcome back, $username!";

    // you can send an email if the login is from a new location
});

Šeit, kad 'user.login' notikums tiek izraisīts, tas sveiks lietotāju vārdā un varētu ietvert loģiku e-pasta nosūtīšanai, ja nepieciešams.

Piezīme: Atsaukums var būt funkcija, anonīma funkcija vai klases metode.

Notikumu izraisīšana

Lai notikums notiktu, izmantojiet Flight::triggerEvent(). Tas saka Flight izpildīt visus klausītājus, kas reģistrēti tam notikumam, nododot līdzi jebkādu datu, ko jūs nododiet.

Flight::triggerEvent(string $event, ...$args): void

Vienkāršs piemērs

$username = 'alice';
Flight::triggerEvent('user.login', $username);

Tas izraisa 'user.login' notikumu un nosūta 'alice' klausītājam, ko mēs definējām iepriekš, kas izvadīs: Welcome back, alice!.

Notikumu apturēšana

Ja klausītājs atgriež false, papildu klausītāji tam notikumam netiks izpildīti. Tas ļauj apturēt notikumu ķēdi, balstoties uz specifiskiem nosacījumiem. Atcerieties, ka klausītāju secība ir svarīga, jo pirmais, kas atgriež false, apturēs pārējos.

Piemērs:

Flight::onEvent('user.login', function ($username) {
    if (isBanned($username)) {
        logoutUser($username);
        return false; // Stops subsequent listeners
    }
});
Flight::onEvent('user.login', function ($username) {
    sendWelcomeEmail($username); // this is never sent
});

Notikumu metožu pārdefinēšana

Flight::onEvent() un Flight::triggerEvent() ir pieejamas paplašināšanai, kas nozīmē, ka jūs varat pārdefinēt, kā tās darbojas. Tas ir lieliski attīstītiem lietotājiem, kas vēlas pielāgot notikumu sistēmu, piemēram, pievienojot reģistrēšanu vai mainot, kā notikumi tiek izplatīti.

Piemērs: onEvent pielāgošana

Flight::map('onEvent', function (string $event, callable $callback) {
    // Log every event registration
    error_log("New event listener added for: $event");
    // Call the default behavior (assuming an internal event system)
    Flight::_onEvent($event, $callback);
});

Tagad katru reizi, kad jūs reģistrējat notikumu, tas to reģistrē pirms turpināšanas.

Kāpēc pārdefinēt?

Kur novietot savus notikumus

Ja jūs esat jauns notikumu konceptos savā projektā, jūs varētu brīnīties: kur es reģistrēju visus šos notikumus savā lietojumprogrammā? Flight vienkāršība nozīmē, ka nav stingra noteikuma — jūs varat tos novietot kur vien tas ir loģiski jūsu projektam. Tomēr to organizēšana palīdz uzturēt jūsu kodu, kad jūsu lietojumprogramma aug. Lūk, daži praktiski varianti un labākās prakses, pielāgotas Flight vieglajai dabai:

Opcija 1: Jūsu galvenajā index.php

Mazām lietojumprogrammām vai ātriem prototipiem jūs varat reģistrēt notikumus tieši jūsu index.php failā blakus jūsu maršrutiem. Tas uztur visu vienā vietā, kas ir labi, kad vienkāršība ir jūsu prioritāte.

require 'vendor/autoload.php';

// Register events
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in at " . date('Y-m-d H:i:s'));
});

// Define routes
Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Logged in!";
});

Flight::start();

Opcija 2: Atsevišķs events.php fails

Nedaudz lielākai lietojumprogrammai apsveriet notikumu reģistrāciju pārvietošanu uz veltītu failu, piemēram, app/config/events.php. Iekļaujiet šo failu jūsu index.php pirms jūsu maršrutiem. Tas imitē, kā maršruti bieži ir organizēti app/config/routes.php Flight projektos.

// app/config/events.php
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in at " . date('Y-m-d H:i:s'));
});

Flight::onEvent('user.registered', function ($email, $name) {
    echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';

Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Logged in!";
});

Flight::start();

Opcija 3: Tuvu tam, kur tie tiek izraisīti

Vēl viena pieeja ir reģistrēt notikumus tuvu vietai, kur tie tiek izraisīti, piemēram, iekšā kontrolierī vai maršruta definīcijā. Tas labi darbojas, ja notikums ir specifisks vienai jūsu lietojumprogrammas daļai.

Flight::route('/signup', function () {
    // Register event here
    Flight::onEvent('user.registered', function ($email) {
        echo "Welcome email sent to $email!";
    });

    $email = 'jane@example.com';
    Flight::triggerEvent('user.registered', $email);
    echo "Signed up!";
});

Labākā prakse Flight

Padoms: Grupēt pēc mērķa

events.php failā grupējiet saistītus notikumus (piemēram, visus lietotāju saistītos notikumus kopā) ar komentāriem skaidrībai:

// app/config/events.php
// User Events
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in");
});
Flight::onEvent('user.registered', function ($email) {
    echo "Welcome to $email!";
});

// Page Events
Flight::onEvent('page.updated', function ($pageId) {
    Flight::cache()->delete("page_$pageId");
});

Šī struktūra labi paplašinās un paliek iesācējam draudzīga.

Reālas pasaules piemēri

Apskatīsim dažus reālas pasaules scenārijus, lai parādītu, kā notikumi darbojas un kāpēc tie ir noderīgi.

Piemērs 1: Lietotāja pieteikšanās reģistrēšana

// Step 1: Register a listener
Flight::onEvent('user.login', function ($username) {
    $time = date('Y-m-d H:i:s');
    error_log("$username logged in at $time");
});

// Step 2: Trigger it in your app
Flight::route('/login', function () {
    $username = 'bob'; // Pretend this comes from a form
    Flight::triggerEvent('user.login', $username);
    echo "Hi, $username!";
});

Kāpēc tas ir noderīgs: Pieteikšanās kods nav jāzina par reģistrēšanu — tas tikai izraisa notikumu. Vēlāk jūs varat pievienot vairāk klausītāju (piemēram, nosūtīt sveiciena e-pastu) bez maršruta maiņas.

Piemērs 2: Paziņošana par jauniem lietotājiem

// Listener for new registrations
Flight::onEvent('user.registered', function ($email, $name) {
    // Simulate sending an email
    echo "Email sent to $email: Welcome, $name!";
});

// Trigger it when someone signs up
Flight::route('/signup', function () {
    $email = 'jane@example.com';
    $name = 'Jane';
    Flight::triggerEvent('user.registered', $email, $name);
    echo "Thanks for signing up!";
});

Kāpēc tas ir noderīgs: Reģistrācijas loģika fokusējas uz lietotāja izveidošanu, kamēr notikums apstrādā paziņojumus. Jūs varētu vēlāk pievienot vairāk klausītāju (piemēram, reģistrēt reģistrāciju).

Piemērs 3: Keša dzēšana

// Listener to clear a cache
Flight::onEvent('page.updated', function ($pageId) {
    // if using the flightphp/cache plugin
    Flight::cache()->delete("page_$pageId");
    echo "Cache cleared for page $pageId.";
});

// Trigger when a page is edited
Flight::route('/edit-page/(@id)', function ($pageId) {
    // Pretend we updated the page
    Flight::triggerEvent('page.updated', $pageId);
    echo "Page $pageId updated.";
});

Kāpēc tas ir noderīgs: Rediģēšanas kods neuztraucas par kešošanu — tas tikai signalizē atjauninājumu. Citas lietojumprogrammas daļas var reaģēt pēc vajadzības.

Labākās prakses

Flight PHP notikumu sistēma ar Flight::onEvent() un Flight::triggerEvent() sniedz jums vienkāršu, bet spēcīgu veidu, kā būvēt elastīgas lietojumprogrammas. Ļaujot dažādām jūsu lietojumprogrammas daļām sazināties caur notikumiem, jūs varat uzturēt savu kodu organizētu, atkārtoti izmantojamu un viegli paplašināmu. Vai nu jūs reģistrējat darbības, sūtat paziņojumus vai pārvaldāt atjauninājumus, notikumi palīdz to darīt bez jūsu loģikas savijuma. Turklāt, ar iespēju pārdefinēt šīs metodes, jums ir brīvība pielāgot sistēmu savām vajadzībām. Sāciet mazu ar vienu notikumu un skatieties, kā tas pārveido jūsu lietojumprogrammas struktūru!

Iebūvētie notikumi

Flight PHP nāk ar dažiem iebūvētiem notikumiem, ko jūs varat izmantot, lai savienotos ar ietvara dzīves ciklu. Šie notikumi tiek izraisīti specifiskos pieprasījuma/atbildes cikla punktos, ļaujot jums izpildīt pielāgotu loģiku, kad notiek noteiktas darbības.

Iebūvēto notikumu saraksts

Skatīt arī

Problēmu risināšana

Izmaiņu žurnāls

Learn/templates

HTML skati un veidnes

Pārskats

Flight pēc noklusējuma nodrošina pamata HTML veidņu funkcionalitāti. Veidņu izmantošana ir ļoti efektīvs veids, kā atdalīt lietojumprogrammas loģiku no prezentācijas slāņa. Īpašs dzinējs (Twig, Latte utt.) arī sniedz AI kodēšanas rīkiem pazīstamu, ierobežotu sintaksi, tāpēc tie mazāk iemaisīs biznesa loģiku jūsu HTML.

Izpratne

Veidojot lietojumprogrammu, jums, visticamāk, būs HTML, ko vēlēsities nosūtīt atpakaļ gala lietotājam. PHP pats par sevi ir veidņu valoda, bet tajā ir ļoti viegli iepīt biznesa loģiku, piemēram, datubāzes izsaukumus, API izsaukumus utt., jūsu HTML failā, padarot testēšanu un atsaistīšanu par ļoti sarežģītu procesu. Ievietojot datus veidnē un ļaujot veidnei sevi atveidot, kļūst daudz vieglāk atsaistīt un vienību testēt savu kodu. Jūs mums pateiksieties, ja izmantosiet veidnes!

Pamata lietošana

Flight ļauj nomainīt noklusējuma skatu dzinēju, vienkārši kartējot render (vai reģistrējot skatu klasi). Ritiniet uz leju, lai redzētu Twig, Latte, Smarty, Blade un citus.

Skeleton noklusējums: Oficiālais flightphp/skeleton izmanto tikai Twig mapē app/views/ (*.twig). Kontrolleri izsauc $this->app->render('welcome', $data) (paplašinājums nav obligāts). Tā ir lietojumprogrammas izvēle jauniem projektiem—nevis Flight kodola prasība. Latte un citi dzinēji joprojām tiek pilnībā atbalstīti.

Twig

skeleton noklusējums

Twig ir elastīgs, ātrs un drošs veidņu dzinējs, ko izmanto Symfony un daudzi citi PHP projekti. AI kodēšanas rīki mēdz īpaši labi pārzināt Twig, un tas pēc noklusējuma automātiski izbēg izvadi, kas palīdz aizsargāties pret XSS.

Instalēšana

composer require twig/twig

(Jau iekļauts, kad veicat composer create-project flightphp/skeleton.)

Pamata konfigurācija

Pārrakstiet render metodi, lai izmantotu Twig, nevis noklusējuma PHP renderētāju:

// pārrakstiet render metodi, lai izmantotu Twig, nevis noklusējuma PHP renderētāju
Flight::map('render', function(string $template, array $data): void {
    $loader = new \Twig\Loader\FilesystemLoader(Flight::get('flight.views.path'));
    $twig = new \Twig\Environment($loader, [
        // Kur Twig glabā savas kompilētās veidnes
        'cache' => __DIR__ . '/../cache/twig',
        'auto_reload' => true,
    ]);

    // Atļauj "welcome" vai "welcome.twig"
    if (substr($template, -5) !== '.twig') {
        $template .= '.twig';
    }

    echo $twig->render($template, $data);
});

Skeleton šis savienojums atrodas app/config/services.php (kopīga Twig vide, kešatmiņas ceļš, globālie mainīgie, piemēram, base_url / CSP nonce). Labāk injicējiet Engine un izsauciet $app->render() no kontrolleriem, lai kods paliktu AI- un testiem draudzīgs.

Twig izmantošana Flight

Tagad, kad varat renderēt ar Twig, varat darīt, piemēram, šādi:

{# app/views/home.twig #}
<html>
  <head>
    <title>{% if title %}{{ title }} - {% endif %}My App</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Hello, {{ name }}!</h1>
  </body>
</html>
// routes.php
Flight::route('/@name', function ($name) {
    Flight::render('home.twig', [
        'title' => 'Home Page',
        'name' => $name
    ]);
});

Kad pārlūkprogrammā apmeklējat /Bob, izvade būtu:

<html>
  <head>
    <title>Home Page - My App</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Hello, Bob!</h1>
  </body>
</html>

Papildu lasīšana

Pilnīgāks Twig izmantošanas piemērs ar izkārtojumiem ir parādīts šīs dokumentācijas awesome plugins sadaļā. Lai redzētu renderēšanas laika metriku Tracy joslā, skatiet Twig paneli Tracy Extensions.

Vairāk par Twig pilnajām iespējām varat uzzināt, lasot oficiālo dokumentāciju.

Latte

lieliska alternatīva

Latte ir pilnvērtīgs dzinējs ar PHP līdzīgu sintaksi. Tas joprojām ir lieliska izvēle Flight lietotnēm; skeleton vienkārši standartizē Twig kā vienu kopīgu noklusējumu (īpaši noderīgi, kad AI rīki ģenerē veidnes).

Instalēšana

composer require latte/latte

Pamata konfigurācija

Galvenā doma ir pārrakstīt render metodi, lai izmantotu Latte, nevis noklusējuma PHP renderētāju.

// pārrakstiet render metodi, lai izmantotu latte, nevis noklusējuma PHP renderētāju
Flight::map('render', function(string $template, array $data, ?string $block): void {
    $latte = new Latte\Engine;

    // Kur latte tieši glabā savu kešatmiņu
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    $finalPath = Flight::get('flight.views.path') . $template;

    $latte->render($finalPath, $data, $block);
});

Latte izmantošana Flight

Tagad, kad varat renderēt ar Latte, varat darīt, piemēram, šādi:

<!-- app/views/home.latte -->
<html>
  <head>
    <title>{$title ? $title . ' - '}My App</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Hello, {$name}!</h1>
  </body>
</html>
// routes.php
Flight::route('/@name', function ($name) {
    Flight::render('home.latte', [
        'title' => 'Home Page',
        'name' => $name
    ]);
});

Kad pārlūkprogrammā apmeklējat /Bob, izvade būtu:

<html>
  <head>
    <title>Home Page - My App</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Hello, Bob!</h1>
  </body>
</html>

Papildu lasīšana

Sarežģītāks Latte izmantošanas piemērs ar izkārtojumiem ir parādīts šīs dokumentācijas awesome plugins sadaļā.

Vairāk par Latte pilnajām iespējām, tostarp tulkošanas un valodu iespējām, varat uzzināt, lasot oficiālo dokumentāciju.

Iebūvētais skatu dzinējs

novecojis

Piezīme: Lai gan tā joprojām ir noklusējuma funkcionalitāte un tehniski joprojām darbojas.

Lai attēlotu skata veidni, izsauciet render metodi ar veidnes faila nosaukumu un neobligātiem veidnes datiem:

Flight::render('hello.php', ['name' => 'Bob']);

Veidnes dati, ko nododat, tiek automātiski ievadīti veidnē, un uz tiem var atsaukties kā uz lokāliem mainīgajiem. Veidņu faili ir vienkārši PHP faili. Ja hello.php veidnes faila saturs ir:

Hello, <?= $name ?>!

Izvade būtu:

Hello, Bob!

Varat arī manuāli iestatīt skata mainīgos, izmantojot set metodi:

Flight::view()->set('name', 'Bob');

Mainīgais name tagad ir pieejams visos jūsu skatos. Tātad varat vienkārši darīt:

Flight::render('hello');

Ņemiet vērā, ka, norādot veidnes nosaukumu render metodē, varat izlaist .php paplašinājumu.

Pēc noklusējuma Flight meklēs views direktoriju veidņu failiem. Varat iestatīt citu ceļu savām veidnēm, iestatot šādu konfigurāciju:

Flight::set('flight.views.path', '/path/to/views');

Izkārtojumi

Tīmekļa vietnēm ir izplatīts vienots izkārtojuma veidnes fails ar mainīgu saturu. Lai renderētu saturu, kas tiks izmantots izkārtojumā, varat nodot neobligātu parametru render metodei.

Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');

Jūsu skatam tad būs saglabātie mainīgie ar nosaukumiem headerContent un bodyContent. Pēc tam varat renderēt savu izkārtojumu šādi:

Flight::render('layout', ['title' => 'Home Page']);

Ja veidņu faili izskatās šādi:

header.php:

<h1><?= $heading ?></h1>

body.php:

<div><?= $body ?></div>

layout.php:

<html>
  <head>
    <title><?= $title ?></title>
  </head>
  <body>
    <?= $headerContent ?>
    <?= $bodyContent ?>
  </body>
</html>

Izvade būtu:

<html>
  <head>
    <title>Home Page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div>World</div>
  </body>
</html>

Smarty

Lūk, kā jūs varētu izmantot Smarty veidņu dzinēju saviem skatiem:

// Ielādē Smarty bibliotēku
require './Smarty/libs/Smarty.class.php';

// Reģistrē Smarty kā skatu klasi
// Nodod arī atzvanīšanas funkciju, lai konfigurētu Smarty ielādes laikā
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
  $smarty->setTemplateDir('./templates/');
  $smarty->setCompileDir('./templates_c/');
  $smarty->setConfigDir('./config/');
  $smarty->setCacheDir('./cache/');
});

// Piešķir veidnes datus
Flight::view()->assign('name', 'Bob');

// Attēlo veidni
Flight::view()->display('hello.tpl');

Lai nodrošinātu pilnīgumu, jums vajadzētu arī pārrakstīt Flight noklusējuma render metodi:

Flight::map('render', function(string $template, array $data): void {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Blade

Lūk, kā jūs varētu izmantot Blade veidņu dzinēju saviem skatiem:

Pirmkārt, jums ir jāinstalē BladeOne bibliotēka, izmantojot Composer:

composer require eftec/bladeone

Pēc tam varat konfigurēt BladeOne kā skatu klasi Flight:

<?php
// Ielādē BladeOne bibliotēku
use eftec\bladeone\BladeOne;

// Reģistrē BladeOne kā skatu klasi
// Nodod arī atzvanīšanas funkciju, lai konfigurētu BladeOne ielādes laikā
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
  $views = __DIR__ . '/../views';
  $cache = __DIR__ . '/../cache';

  $blade->setPath($views);
  $blade->setCompiledPath($cache);
});

// Piešķir veidnes datus
Flight::view()->share('name', 'Bob');

// Attēlo veidni
echo Flight::view()->run('hello', []);

Lai nodrošinātu pilnīgumu, jums vajadzētu arī pārrakstīt Flight noklusējuma render metodi:

<?php
Flight::map('render', function(string $template, array $data): void {
  echo Flight::view()->run($template, $data);
});

Šajā piemērā hello.blade.php veidnes fails varētu izskatīties šādi:

<?php
Hello, {{ $name }}!

Izvade būtu:

Hello, Bob!

Skatīt arī

Problēmu novēršana

Izmaiņu žurnāls

Learn/simple_pdo

SimplePdo PDO Palīdze Ģimene

Pārskats

SimplePdo klase Flight ir moderna, funkcijām bagāta palīdze darbam ar datubāzēm, izmantojot PDO. Tā paplašina PdoWrapper un pievieno ērtas palīgmēģenēm izplatītām datubāzes operācijām, piemēram, insert(), update(), delete() un transakcijām. Tā vienkāršo datubāzes uzdevumus, atgriež rezultātus kā Collections vieglai piekļuvei un atbalsta vaicājumu žurnālveidošanu un lietojumprogrammas veiktspējas uzraudzību (APM) sarežģītām lietošanas situācijām.

Saprašana

SimplePdo klase ir izstrādāta, lai padarītu darbu ar datubāzēm PHP daudz vieglāku. Tā vietā, lai žonglētu ar sagatavotiem paziņojumiem, iegūšanas režīmiem un verbāliem SQL operācijām, jūs saņemat tīras, vienkāršas metodes izplatītiem uzdevumiem. Katra rindiņa tiek atgriezta kā Collection, tāpēc jūs varat izmantot gan masīva notāciju ($row['name']), gan objekta notāciju ($row->name).

Šī klase ir PdoWrapper pārklājums, kas nozīmē, ka tā ietver visu PdoWrapper funkcionalitāti plus papildu palīgmēģenes, kas padara jūsu kodu tīrāku un vieglāk uzturamu. Ja jūs pašlaik izmantojat PdoWrapper, pāreja uz SimplePdo ir vienkārša, jo tā paplašina PdoWrapper.

Jūs varat reģistrēt SimplePdo kā kopīgu servisu Flight, un tad izmantot to jebkur savā lietojumprogrammā caur Flight::db().

Pamata Lietošana

Reģistrēšana SimplePdo

Vispirms reģistrējiet SimplePdo klasi Flight:

Flight::register('db', \flight\database\SimplePdo::class, [
    'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
]);

PIEZĪME

Ja jūs ne norādāt PDO::ATTR_DEFAULT_FETCH_MODE, SimplePdo automātiski iestatīs to uz PDO::FETCH_ASSOC jūsu vietā.

Tagad jūs varat izmantot Flight::db() jebkur, lai iegūtu savienojumu ar datubāzi.

Vaicājumu Izpilde

runQuery()

function runQuery(string $sql, array $params = []): PDOStatement

Izmantojiet to INSERT, UPDATE vai kad vēlaties pašrocīgi iegūt rezultātus:

$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM users WHERE status = ?", ['active']);
while ($row = $statement->fetch()) {
    // $row ir masīvs
}

Jūs varat izmantot to arī rakstīšanai:

$db->runQuery("INSERT INTO users (name) VALUES (?)", ['Alice']);
$db->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 1]);

fetchField()

function fetchField(string $sql, array $params = []): mixed

Iegūstiet vienu vērtību no datubāzes:

$count = Flight::db()->fetchField("SELECT COUNT(*) FROM users WHERE status = ?", ['active']);

fetchRow()

function fetchRow(string $sql, array $params = []): ?Collection

Iegūstiet vienu rindiņu kā Collection (masīva/objekta piekļuve):

$user = Flight::db()->fetchRow("SELECT * FROM users WHERE id = ?", [123]);
echo $user['name'];
// vai
echo $user->name;

PADOMS

SimplePdo automātiski pievieno LIMIT 1 vaicājumiem fetchRow(), ja tas vēl nav klāt, padarot jūsu vaicājumus efektīvākus.

fetchAll()

function fetchAll(string $sql, array $params = []): array<Collection>

Iegūstiet visas rindiņas kā Collection masīvu:

$users = Flight::db()->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
foreach ($users as $user) {
    echo $user['name'];
    // vai
    echo $user->name;
}

fetchColumn()

function fetchColumn(string $sql, array $params = []): array

Iegūstiet vienu kolonnu kā masīvu:

$ids = Flight::db()->fetchColumn("SELECT id FROM users WHERE active = ?", [1]);
// Atgriež: [1, 2, 3, 4, 5]

fetchPairs()

function fetchPairs(string $sql, array $params = []): array

Iegūstiet rezultātus kā atslēgu-vērtību pārus (pirmā kolonna kā atslēga, otrā kā vērtība):

$userNames = Flight::db()->fetchPairs("SELECT id, name FROM users");
// Atgriež: [1 => 'John', 2 => 'Jane', 3 => 'Bob']

Izmantojot IN() Vietas Turētājus

Jūs varat izmantot vienu ? IN() klauzulā un nodot masīvu:

$ids = [1, 2, 3];
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", [$ids]);

Palīgmēģenes

Viens no galvenajiem SimplePdo priekšrocībām salīdzinājumā ar PdoWrapper ir ērtu palīgmēģeņu pievienošana izplatītām datubāzes operācijām.

insert()

function insert(string $table, array $data): string

Ievietojiet vienu vai vairākas rindiņas un atgrieziet pēdējo ievietošanas ID.

Viena ievietošana:

$id = Flight::db()->insert('users', [
    'name' => 'John',
    'email' => 'john@example.com'
]);

Masveida ievietošana:

$id = Flight::db()->insert('users', [
    ['name' => 'John', 'email' => 'john@example.com'],
    ['name' => 'Jane', 'email' => 'jane@example.com'],
]);

update()

function update(string $table, array $data, string $where, array $whereParams = []): int

Atjauniniet rindiņas un atgrieziet skarto rindiņu skaitu:

$affected = Flight::db()->update(
    'users',
    ['name' => 'Jane', 'email' => 'jane@example.com'],
    'id = ?',
    [1]
);

PIEZĪME

SQLite rowCount() atgriež skaitu rindiņu, kur dati patiešām mainījās. Ja jūs atjauninat rindiņu ar tām pašām vērtībām, kas tai jau ir, rowCount() atgriezīs 0. Tas atšķiras no MySQL uzvedības, izmantojot PDO::MYSQL_ATTR_FOUND_ROWS.

delete()

function delete(string $table, string $where, array $whereParams = []): int

Dzēsiet rindiņas un atgrieziet dzēsto rindiņu skaitu:

$deleted = Flight::db()->delete('users', 'id = ?', [1]);

transaction()

function transaction(callable $callback): mixed

Izpildiet atsaukumu transakcijas ietvaros. Transakcija automātiski apstiprina veiksmīgos gadījumos vai atgriežas kļūdas gadījumā:

$result = Flight::db()->transaction(function($db) {
    $db->insert('users', ['name' => 'John']);
    $db->insert('logs', ['action' => 'user_created']);
    return $db->lastInsertId();
});

Ja jebkura izņēmuma tiek mestas atsaukuma ietvaros, transakcija automātiski atgriežas un izņēmums tiek atkārtoti mestas.

Padziļinātā Lietošana

Vaicājumu Žurnālveidošana & APM

Ja vēlaties izsekot vaicājuma veiktspēju, iespējiet APM izsekošanu reģistrēšanas laikā:

Flight::register('db', \flight\database\SimplePdo::class, [
    'mysql:host=localhost;dbname=cool_db_name',
    'user',
    'pass',
    [/* PDO opcijas */],
    [
        'trackApmQueries' => true,
        'maxQueryMetrics' => 1000
    ]
]);

Pēc vaicājumu izpildes jūs varat žurnālveidot tos manuāli, bet APM tos žurnālveidos automātiski, ja iespējots:

Flight::db()->logQueries();

Tas izraisīs notikumu (flight.db.queries) ar savienojuma un vaicājuma metrikiem, ko jūs varat klausīties, izmantojot Flight notikumu sistēmu.

Pilns Piemērs

Flight::route('/users', function () {
    // Iegūt visus lietotājus
    $users = Flight::db()->fetchAll('SELECT * FROM users');

    // Straumēt visus lietotājus
    $statement = Flight::db()->runQuery('SELECT * FROM users');
    while ($user = $statement->fetch()) {
        echo $user['name'];
    }

    // Iegūt vienu lietotāju
    $user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);

    // Iegūt vienu vērtību
    $count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');

    // Iegūt vienu kolonnu
    $ids = Flight::db()->fetchColumn('SELECT id FROM users');

    // Iegūt atslēgu-vērtību pārus
    $userNames = Flight::db()->fetchPairs('SELECT id, name FROM users');

    // Īpaša IN() sintakse
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);

    // Ievietot jaunu lietotāju
    $id = Flight::db()->insert('users', [
        'name' => 'Bob',
        'email' => 'bob@example.com'
    ]);

    // Masveida ievietošana lietotājiem
    Flight::db()->insert('users', [
        ['name' => 'Bob', 'email' => 'bob@example.com'],
        ['name' => 'Jane', 'email' => 'jane@example.com']
    ]);

    // Atjaunināt lietotāju
    $affected = Flight::db()->update('users', ['name' => 'Bob'], 'id = ?', [123]);

    // Dzēst lietotāju
    $deleted = Flight::db()->delete('users', 'id = ?', [123]);

    // Izmantot transakciju
    $result = Flight::db()->transaction(function($db) {
        $db->insert('users', ['name' => 'John', 'email' => 'john@example.com']);
        $db->insert('audit_log', ['action' => 'user_created']);
        return $db->lastInsertId();
    });
});

Migrēšana no PdoWrapper

Ja jūs pašlaik izmantojat PdoWrapper, migrēšana uz SimplePdo ir vienkārša:

  1. Atjauniniet savu reģistrāciju:

    // Vecais
    Flight::register('db', \flight\database\PdoWrapper::class, [ /* ... */ ]);
    
    // Jaunais
    Flight::register('db', \flight\database\SimplePdo::class, [ /* ... */ ]);
  2. Visas esošās PdoWrapper metodes darbojas SimplePdo - Nav laušanas izmaiņu. Jūsu esošais kods turpinās darboties.

  3. Pēc izvēles izmantojiet jaunas palīgmēģenes - Sāciet izmantot insert(), update(), delete() un transaction(), lai vienkāršotu savu kodu.

Skatīt Arī

Traucējummeklēšana

Izmaiņu Žurnāls

Learn/collections

Kolekcijas

Pārskats

Collection klase Flight ietvaros ir ērts rīks datu kopu pārvaldīšanai. Tā ļauj piekļūt datiem un tos manipulēt, izmantojot gan masīvu, gan objektu pierakstu, padarot jūsu kodu tīrāku un elastīgāku.

Izpratne

Collection būtībā ir masīva iesaiņotājs, taču ar dažām papildu iespējām. Jūs to varat izmantot kā masīvu, veikt cilpu caur to, saskaitīt tā vienumus un pat piekļūt vienumiem tā, it kā tie būtu objekta īpašības. Tas ir īpaši noderīgi, ja vēlaties nodot strukturētus datus savā lietotnē vai padarīt savu kodu lasāmāku.

Kolekcijas implementē vairākas PHP saskarnes:

Pamata Lietošana

Kolekcijas Izveide

Jūs varat izveidot kolekciju, vienkārši nododot masīvu tās konstruktoram:

use flight\util\Collection;

$data = [
  'name' => 'Flight',
  'version' => 3,
  'features' => ['routing', 'views', 'extending']
];

$collection = new Collection($data);

Piekļuve Vienumiem

Jūs varat piekļūt vienumiem, izmantojot vai nu masīva, vai objekta pierakstu:

// Masīva pieraksts
echo $collection['name']; // Izvade: FlightPHP

// Objekta pieraksts
echo $collection->version; // Izvade: 3

Vienumu Iestatīšana

Jūs varat iestatīt vienumus, izmantojot arī jebkuru no pierakstiem:

// Masīva pieraksts
$collection['author'] = 'Mike Cao';

// Objekta pieraksts
$collection->license = 'MIT';

Vienumu Pārbaude un Noņemšana

Pārbaudiet, vai vienums pastāv:

if (isset($collection['name'])) {
  // Dariet kaut ko
}

if (isset($collection->version)) {
  // Dariet kaut ko
}

Noņemiet vienumu:

unset($collection['author']);
unset($collection->license);

Cilpa Caur Kolekciju

Kolekcijas ir iterējamas, tāpēc jūs tās varat izmantot foreach cilpā:

foreach ($collection as $key => $value) {
  echo "$key: $value\n";
}

Vienumu Skaitīšana

Jūs varat saskaitīt vienumu skaitu kolekcijā:

echo count($collection); // Izvade: 4

Visu Atslēgu vai Datu Iegūšana

Iegūt visas atslēgas:

$keys = $collection->keys(); // ['name', 'version', 'features', 'license']

Iegūt visus datus kā masīvu:

$data = $collection->getData();

Kolekcijas Notīrīšana

Noņemt visus vienumus:

$collection->clear();

JSON Serializācija

Kolekcijas var viegli pārveidot uz JSON:

echo json_encode($collection);
// Izvade: {"name":"FlightPHP","version":3,"features":["routing","views","extending"],"license":"MIT"}

Papildu Lietošana

Jūs varat pilnībā aizstāt iekšējo datu masīvu, ja nepieciešams:

$collection->setData(['foo' => 'bar']);

Kolekcijas ir īpaši noderīgas, ja vēlaties nodot strukturētus datus starp komponentiem vai nodrošināt vairāk objektorientētu saskarni masīvu datiem.

Skatīt Arī

Problēmu Novēršana

Izmaiņu Žurnāls

Learn/flight_vs_fat_free

Flight pret Fat-Free

Kas ir Fat-Free?

Fat-Free (mīļi saukts par F3) ir jaudīgs, tomēr viegli lietojams PHP mikroietvars, kas izstrādāts, lai palīdzētu jums veidot dinamiskas un stabilas tīmekļa lietotnes - ātri!

Flight daudzējādā ziņā salīdzinās ar Fat-Free un, iespējams, ir vistuvākais radinieks funkciju un vienkāršības ziņā. Fat-Free ir daudz funkciju, kuru Flight nav, bet tam ir arī daudz funkciju, kuras Flight ir. Fat-Free sāk parādīt savu vecumu un vairs nav tik populārs kā tas bija kādreiz.

Atjauninājumi kļūst arvien retāki, un kopiena vairs nav tik aktīva kā agrāk. Kods ir pietiekami vienkāršs, bet dažkārt sintakses disciplīnas trūkums var apgrūtināt tā lasīšanu un saprašanu. Tas darbojas PHP 8.3, bet pats kods joprojām izskatās tā, it kā tas būtu no PHP 5.3 laikiem.

Plusi salīdzinājumā ar Flight

Mīnusi salīdzinājumā ar Flight

Learn/extending

Paplašināšana

Pārskats

Flight ir izstrādāts kā paplašināms ietvars. Ietvars nāk ar kopu noklusējuma metožu un komponentu, bet tas ļauj jums kartēt savas metodes, reģistrēt savas klases vai pat pārrakstīt esošās klases un metodes.

Saprašana

Ir 2 veidi, kā jūs varat paplašināt Flight funkcionalitāti:

  1. Metožu kartēšana - Tas tiek izmantots, lai izveidotu vienkāršas pielāgotas metodes, kuras jūs varat izsaukt no jebkuras vietas savā lietojumprogrammā. Šīs parasti tiek izmantotas utilītas funkcijām, kuras jūs vēlaties varēt izsaukt no jebkuras vietas savā kodā.
  2. Klases reģistrēšana - Tas tiek izmantots, lai reģistrētu savas klases ar Flight. Šis parasti tiek izmantots klasēm, kurām ir atkarības vai nepieciešama konfigurācija.

Jūs varat arī pārrakstīt esošās ietvara metodes, lai mainītu to noklusējuma uzvedību, lai labāk atbilstu jūsu projekta vajadzībām.

Ja jūs meklējat DIC (Atkarību injekcijas konteineru), pārejiet uz Dependency Injection Container lapu.

Pamata lietošana

Ietvara metožu pārrakstīšana

Flight ļauj jums pārrakstīt tā noklusējuma funkcionalitāti, lai tā atbilstu jūsu vajadzībām, bez nepieciešamības modificēt jebkuru kodu. Jūs varat apskatīt visas metodes, kuras varat pārrakstīt zemāk.

Piemēram, kad Flight nevar saskaņot URL ar maršrutu, tas izsauc notFound metodi, kas nosūta vispārīgu HTTP 404 atbildi. Jūs varat pārrakstīt šo uzvedību, izmantojot map metodi:

Flight::map('notFound', function() {
  // Rādīt pielāgotu 404 lapu
  include 'errors/404.html';
});

Flight arī ļauj jums aizstāt ietvara kodola komponentus. Piemēram, jūs varat aizstāt noklusējuma Router klasi ar savu pielāgotu klasi:

// izveidot savu pielāgoto Router klasi
class MyRouter extends \flight\net\Router {
    // pārrakstīt metodes šeit
    // piemēram, saīsinājums GET pieprasījumiem, lai noņemtu
    // pass route funkciju
    public function get($pattern, $callback, $alias = '') {
        return parent::get($pattern, $callback, false, $alias);
    }
}

// Reģistrēt savu pielāgoto klasi
Flight::register('router', MyRouter::class);

// Kad Flight ielādē Router экземпlāru, tas ielādēs jūsu klasi
$myRouter = Flight::router();
$myRouter->get('/hello', function() {
  echo "Hello World!";
}, 'hello_alias');

Tomēr ietvara metodes, piemēram map un register, nevar tikt pārrakstītas. Jūs saņemsiet kļūdu, ja mēģināsiet to izdarīt (atkal skatiet zemāk metožu sarakstam).

Kartējamās ietvara metodes

Turpmāk ir pilnīga metožu kopa ietvaram. Tā sastāv no kodola metodēm, kuras ir regulāras statiskas metodes, un paplašināmām metodēm, kuras ir kartētas metodes, kuras var filtrēt vai pārrakstīt.

Kodola metodes

Šīs metodes ir kodola ietvaram un nevar tikt pārrakstītas.

Flight::map(string $name, callable $callback, bool $pass_route = false) // Izveido pielāgotu ietvara metodi.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Reģistrē klasi ietvara metodē.
Flight::unregister(string $name) // Izslēdz klasi no ietvara metodes.
Flight::before(string $name, callable $callback) // Pievieno filtru pirms ietvara metodes.
Flight::after(string $name, callable $callback) // Pievieno filtru pēc ietvara metodes.
Flight::path(string $path) // Pievieno ceļu automātiskai klases ielādei.
Flight::get(string $key) // Iegūst mainīgo, ko iestatījis Flight::set().
Flight::set(string $key, mixed $value) // Iestata mainīgo Flight dzinējā.
Flight::has(string $key) // Pārbauda, vai mainīgais ir iestatīts.
Flight::clear(array|string $key = []) // Notīra mainīgo.
Flight::init() // Inicializē ietvaru ar tā noklusējuma iestatījumiem.
Flight::app() // Iegūst lietojumprogrammas objekta экземпlāru
Flight::request() // Iegūst pieprasījuma objekta экземпlāru
Flight::response() // Iegūst atbildes objekta экземпlāru
Flight::router() // Iegūst maršrutētāja objekta экземпlāru
Flight::view() // Iegūst skata objekta экземпlāru

Paplašināmas metodes

Flight::start() // Sāk ietvaru.
Flight::stop() // Aptur ietvaru un nosūta atbildi.
Flight::halt(int $code = 200, string $message = '') // Aptur ietvaru ar opcionālu statusa kodu un ziņu.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Kartē URL paraugu ar atsaukumu.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Kartē POST pieprasījuma URL paraugu ar atsaukumu.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Kartē PUT pieprasījuma URL paraugu ar atsaukumu.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Kartē PATCH pieprasījuma URL paraugu ar atsaukumu.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Kartē DELETE pieprasījuma URL paraugu ar atsaukumu.
Flight::group(string $pattern, callable $callback) // Izveido grupēšanu URL, paraugam jābūt virknei.
Flight::getUrl(string $name, array $params = []) // Ģenerē URL, balstoties uz maršruta aliasu.
Flight::redirect(string $url, int $code) // Pāradresē uz citu URL.
Flight::download(string $filePath) // Lejupielādē failu.
Flight::render(string $file, array $data, ?string $key = null) // Renderē veidnes failu.
Flight::error(Throwable $error) // Nosūta HTTP 500 atbildi.
Flight::notFound() // Nosūta HTTP 404 atbildi.
Flight::etag(string $id, string $type = 'string') // Veic ETag HTTP kešošanu.
Flight::lastModified(int $time) // Veic pēdējās modificēšanas HTTP kešošanu.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Nosūta JSON atbildi.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Nosūta JSONP atbildi.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Nosūta JSON atbildi un aptur ietvaru.
Flight::onEvent(string $event, callable $callback) // Reģistrē notikuma klausītāju.
Flight::triggerEvent(string $event, ...$args) // Izsauc notikumu.

Jebkuras pielāgotas metodes, kas pievienotas ar map un register, var arī tikt filtrētas. Piemēriem, kā filtrēt šīs metodes, skatiet Filtering Methods rokasgrāmatu.

Paplašināmas ietvara klases

Ir vairākas klases, kurām jūs varat pārrakstīt funkcionalitāti, paplašinot tās un reģistrējot savu klasi. Šīs klases ir:

Flight::app() // Lietojumprogrammas klase - paplašiniet flight\Engine klasi
Flight::request() // Pieprasījuma klase - paplašiniet flight\net\Request klasi
Flight::response() // Atbildes klase - paplašiniet flight\net\Response klasi
Flight::router() // Maršrutētāja klase - paplašiniet flight\net\Router klasi
Flight::view() // Skata klase - paplašiniet flight\template\View klasi
Flight::eventDispatcher() // Notikuma dispečera klase - paplašiniet flight\core\Dispatcher klasi

Pielāgoto metožu kartēšana

Lai kartētu savu vienkāršo pielāgoto metodi, jūs izmantojat map funkciju:

// Kartēt savu metodi
Flight::map('hello', function (string $name) {
  echo "hello $name!";
});

// Izsaukt savu pielāgoto metodi
Flight::hello('Bob');

Lai gan ir iespējams izveidot vienkāršas pielāgotas metodes, ieteicams tikai izveidot standarta funkcijas PHP. Tam ir autocomplēte IDE un tas ir vieglāk lasāms. Iepriekš minētā koda ekvivalents būtu:

function hello(string $name) {
  echo "hello $name!";
}

hello('Bob');

Tas tiek izmantots vairāk, kad jums jānodod mainīgie jūsu metodē, lai iegūtu gaidīto vērtību. Izmantojot register() metodi kā zemāk, tas ir vairāk par konfigurācijas nodošanu un tad izsaukt savu iepriekš konfigurēto klasi.

Pielāto klases reģistrēšana

Lai reģistrētu savu klasi un konfigurētu to, jūs izmantojat register funkciju. Iepriekšējs ieguvums, ko tas sniedz pār map(), ir tas, ka jūs varat atkārtoti izmantot to pašu klasi, kad izsaucat šo funkciju (tas būtu noderīgi ar Flight::db(), lai dalītos tajā pašā экземпlārā).

// Reģistrēt savu klasi
Flight::register('user', User::class);

// Iegūt klases экземпlāru
$user = Flight::user();

Reģistrēšanas metode arī ļauj jums nodot parametrus jūsu klases konstruktoram. Tātad, kad jūs ielādējat savu pielāgoto klasi, tā nāks jau inicializēta. Jūs varat definēt konstruktora parametrus, nododot papildu masīvu. Šeit ir piemērs datubāzes savienojuma ielādei:

// Reģistrēt klasi ar konstruktora parametriem
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);

// Iegūt klases экземпlāru
// Tas izveidos objektu ar definētajiem parametriem
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();

// un ja jums tas būtu vajadzīgs vēlāk savā kodā, jūs tikai izsaucat to pašu metodi vēlreiz
class SomeController {
  public function __construct() {
    $this->db = Flight::db();
  }
}

Ja jūs nododit papildu atsauces parametru, tas tiks izpildīts uzreiz pēc klases konstrukcijas. Tas ļauj jums veikt jebkuru iestatīšanas procedūru jūsu jaunajam objektam. Atsauces funkcija ņem vienu parametru, jauna objekta экземпlāru.

// Atsauce tiks nodota konstrukcijas objekts
Flight::register(
  'db',
  PDO::class,
  ['mysql:host=localhost;dbname=test', 'user', 'pass'],
  function (PDO $db) {
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
);

Pēc noklusējuma katru reizi, kad jūs ielādējat savu klasi, jūs iegūsit kopīgu экземпlāru. Lai iegūtu jaunu klases экземпlāru, vienkārši nododit false kā parametru:

// Kopīgs klases экземпlārs
$shared = Flight::db();

// Jauns klases экземпlārs
$new = Flight::db(false);

Piezīme: Ņemiet vērā, ka kartētās metodes ir priekšroka pār reģistrētajām klasēm. Ja jūs deklarējat abas, izmantojot to pašu nosaukumu, tikai kartētā metode tiks izsaukta.

Piemēri

Šeit ir daži piemēri, kā jūs varat paplašināt Flight ar funkcionalitāti, kas nav iebūvēta kodolā.

Žurnālveide

Flight nav iebūvēta žurnālveides sistēma, tomēr ir patiešām viegli izmantot žurnālveides bibliotēku ar Flight. Šeit ir piemērs, izmantojot Monolog bibliotēku:

// services.php

// Reģistrēt žurnālu ar Flight
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
    $log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});

Tagad, kad tas ir reģistrēts, jūs varat to izmantot savā lietojumprogrammā:

// Jūsu kontrolierī vai maršrutā
Flight::log()->warning('This is a warning message');

Tas ierakstīs ziņu norādītajā žurnāla failā. Ko tad, ja jūs vēlaties ierakstīt kaut ko, kad rodas kļūda? Jūs varat izmantot error metodi:

// Jūsu kontrolierī vai maršrutā
Flight::map('error', function(Throwable $ex) {
    Flight::log()->error($ex->getMessage());
    // Rādīt savu pielāgoto kļūdas lapu
    include 'errors/500.html';
});

Jūs arī varētu izveidot pamata APM (Lietojumprogrammas veiktspējas uzraudzību) sistēmu, izmantojot before un after metodes:

// Jūsu services.php failā

Flight::before('start', function() {
    Flight::set('start_time', microtime(true));
});

Flight::after('start', function() {
    $end = microtime(true);
    $start = Flight::get('start_time');
    Flight::log()->info('Request '.Flight::request()->url.' took ' . round($end - $start, 4) . ' seconds');

    // Jūs varētu arī pievienot savu pieprasījumu vai atbildes galvenes
    // lai ierakstītu tās (esiet uzmanīgs, jo tas būtu daudz datu, ja jums ir daudz pieprasījumu)
    Flight::log()->info('Request Headers: ' . json_encode(Flight::request()->headers));
    Flight::log()->info('Response Headers: ' . json_encode(Flight::response()->headers));
});

Kešošana

Flight nav iebūvēta kešošanas sistēma, tomēr ir patiešām viegli izmantot kešošanas bibliotēku ar Flight. Šeit ir piemērs, izmantojot PHP File Cache bibliotēku:

// services.php

// Reģistrēt kešu ar Flight
Flight::register('cache', \flight\Cache::class, [ __DIR__ . '/../cache/' ], function(\flight\Cache $cache) {
    $cache->setDevMode(ENVIRONMENT === 'development');
});

Tagad, kad tas ir reģistrēts, jūs varat to izmantot savā lietojumprogrammā:

// Jūsu kontrolierī vai maršrutā
$data = Flight::cache()->get('my_cache_key');
if (empty($data)) {
    // Veikt kādu apstrādi, lai iegūtu datus
    $data = [ 'some' => 'data' ];
    Flight::cache()->set('my_cache_key', $data, 3600); // kešot uz 1 stundu
}

Viegla DIC objekta instantiācija

Ja jūs izmantojat DIC (Atkarību injekcijas konteineru) savā lietojumprogrammā, jūs varat izmantot Flight, lai palīdzētu jums instantiēt savus objektus. Šeit ir piemērs, izmantojot Dice bibliotēku:

// services.php

// izveidot jaunu konteineru
$container = new \Dice\Dice;
// neaizmirstiet to atkārtoti piešķirt sev pašam kā zemāk!
$container = $container->addRule('PDO', [
    // shared nozīmē, ka tas pats objekts tiks atgriezts katru reizi
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// tagad mēs varam izveidot kartējamu metodi, lai izveidotu jebkuru objektu. 
Flight::map('make', function($class, $params = []) use ($container) {
    return $container->create($class, $params);
});

// Tas reģistrē konteinera apstrādātāju, lai Flight zinātu to izmantot kontrolieriem/vidējai slānim
Flight::registerContainerHandler(function($class, $params) {
    Flight::make($class, $params);
});


// pieņemsim, ka mums ir šāda parauga klase, kas ņem PDO objektu konstruktorā
class EmailCron {
    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function send() {
        // kods, kas nosūta e-pastu
    }
}

// Un beidzot jūs varat izveidot objektus, izmantojot atkarību injekciju
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();

Eleganti, vai ne?

Skatīt arī

Traucējummeklēšana

Izmaiņu žurnāls

Learn/json

JSON Apvalks

Pārskats

Json klase Flight nodrošina vienkāršu, konsekventu veidu, kā kodēt un dekodēt JSON datus jūsu lietojumprogrammā. Tā apvalko PHP iebūvēto JSON funkciju ar labāku kļūdu apstrādi un dažām noderīgām noklusējuma vērtībām, padarot to vieglāku un drošāku strādāt ar JSON.

Saprašana

Darbs ar JSON ir ārkārtīgi izplatīts mūsdienu PHP lietojumprogrammās, īpaši, kad veidojat API vai apstrādājat AJAX pieprasījumus. Json klase centralizē visu jūsu JSON kodēšanu un dekodēšanu, tāpēc jums nav jāuztraucas par dīvainiem malu gadījumiem vai nesaprotamām kļūdām no PHP iebūvētajām funkcijām.

Galvenās funkcijas:

Pamata Izmantošana

Datu Kodēšana uz JSON

Lai pārveidotu PHP datus uz JSON virkni, izmantojiet Json::encode():

use flight\util\Json;

$data = [
  'framework' => 'Flight',
  'version' => 3,
  'features' => ['routing', 'views', 'extending']
];

$json = Json::encode($data);
echo $json;
// Output: {"framework":"Flight","version":3,"features":["routing","views","extending"]}

Ja kodēšana neizdodas, jūs saņemsiet izņēmumu ar noderīgu kļūdas ziņu.

Skaista Izdruka

Vai vēlaties, lai jūsu JSON būtu lasāms cilvēkiem? Izmantojiet prettyPrint():

echo Json::prettyPrint($data);
/*
{
  "framework": "Flight",
  "version": 3,
  "features": [
    "routing",
    "views",
    "extending"
  ]
}
*/

JSON Virkņu Dekodēšana

Lai pārveidotu JSON virkni atpakaļ uz PHP datiem, izmantojiet Json::decode():

$json = '{"framework":"Flight","version":3}';
$data = Json::decode($json);
echo $data->framework; // Output: Flight

Ja vēlaties asociatīvu masīvu nevis objektu, nododiet true kā otro argumentu:

$data = Json::decode($json, true);
echo $data['framework']; // Output: Flight

Ja dekodēšana neizdodas, jūs saņemsiet izņēmumu ar skaidru kļūdas ziņu.

JSON Validācija

Pārbaudiet, vai virkne ir derīgs JSON:

if (Json::isValid($json)) {
  // Tas ir derīgs!
} else {
  // Nav derīgs JSON
}

Pēdējās Kļūdas Iegūšana

Ja vēlaties pārbaudīt pēdējo JSON kļūdas ziņu (no iebūvētām PHP funkcijām):

$error = Json::getLastError();
if ($error !== '') {
  echo "Last JSON error: $error";
}

Uzlabota Izmantošana

Jūs varat pielāgot kodēšanas un dekodēšanas opcijas, ja vajag vairāk kontroles (skatiet PHP json_encode opcijas):

// Kodēšana ar HEX_TAG opciju
$json = Json::encode($data, JSON_HEX_TAG);

// Dekodēšana ar pielāgotu dziļumu
$data = Json::decode($json, false, 1024);

Skatīt Arī

Traucējummeklēšana

Izmaiņu Žurnāls

Learn/flight_vs_slim

Flight pret Slim

Kas ir Slim?

Slim ir PHP mikro ietvars, kas palīdz ātri izveidot vienkāršas, bet jaudīgas tīmekļa lietojumprogrammas un API.

Daudz iedvesmas dažiem Flight v3 funkcijām faktiski nāca no Slim. Maršrutu grupēšana un starpprogrammatūras izpilde noteiktā secībā ir divas funkcijas, kuras iedvesmoja Slim. Slim v3 tika izlaists ar mērķi uz vienkāršību, bet par v4 ir pretrunīgi vērtējumi.

Priekšrocības salīdzinājumā ar Flight

Trūkumi salīdzinājumā ar Flight

Learn/autoloading

Automātiskā ielāde

Pārskats

Automatiskā ielāde ir PHP koncepcija, kurā norādāt direktoriju vai direktorijas, no kurām ielādēt klases. Tas ir daudz izdevīgāk nekā izmantot require vai include, lai ielādētu klases. Tas ir arī priekšnoteikums Composer pakotņu izmantošanai.

Pareiza automātiskās ielādes iestatīšana ir svarīga arī ar AI atbalstītu izstrādi: aģenti novieto failus tur, kur norāda nosaukumvieta. Ja mapes reģistrs un nosaukumvietas reģistrs nesakrīt, Linux sistēmā parādās kļūdas par klases neatrašanu, pat ja lietas "strādāja" uz Mac diska, kur reģistrs netiek nošķirts.

Izpratne

Pēc noklusējuma jebkura Flight klase tiek ielādēta automātiski, pateicoties Composer. Jūsu lietojumprogrammas klasēm ir divas izplatītas pieejas:

  1. Composer PSR-4 (ko izmanto oficiālais skeletons): kartējiet nosaukumvietas prefiksu uz direktoriju composer.json failā, pēc tam izpildiet composer dump-autoload.
  2. Flight::path(): norādiet Flight ielādētājam direktorijas (noderīgi vienkāršām lietotnēm vai ja neizmantojat Composer lietojumprogrammas kodam).

Automatiskās ielādes izmantošana ievērojami vienkāršo jūsu kodu. Tā vietā, lai katra faila augšpusē būtu vesela siena ar include / require, klases tiek ielādētas, kad tās pirmo reizi izmantojat.

Reģistrjutība (izlasiet šo divreiz)

Nosaukumvietām ir jāatbilst direktoriju struktūrai un šo direktoriju burtu reģistram.

Strādā Nedarbojas Linux
App\Controller\HomeControllerapp/Controller/HomeController.php App\Controller\… ar mapi app/controllers/
app\controllers\MyControllerapp/controllers/MyController.php Ja sajauc App\ ar mazajiem burtiem controllers

PHP nosaukumvietas dažos kontekstos nav reģistrjutīgas, bet Composer un failu sistēma ir. Oficiālais skeletons standartizē šādi:

Vecāki dokumenti un kopienas piemēri dažkārt izmantoja mazos burtus app\controllers. Tas joprojām darbojas, ja jūsu mapes ir mazajos burtos—bet jauni skeletona projekti izmanto App\ + PascalCase mapes. Izvēlieties vienu konvenciju projektam un pieturieties pie tās, lai cilvēki un AI rīki neizgudrotu otru izkārtojumu.

Skeletons (ieteicams jauniem projektiem)

Pēc composer create-project flightphp/skeleton komandas, lietojumprogrammas kods tiek ielādēts, izmantojot Composer—Flight::path() nav nepieciešams App\ klasēm:

{
  "autoload": {
    "psr-4": {
      "App\\": "app/"
    }
  }
}
// app/Controller/HomeController.php
namespace App\Controller;

use flight\Engine;

class HomeController
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function index(): void
    {
        $this->app->render('welcome', ['message' => 'Hello!']);
    }
}
// app/config/routes.php — Dice atrisina App\Controller\… caur konteineru
$router->get('/', [HomeController::class, 'index']);

Skatiet Instalācija, lai redzētu pilnu koku, un AI & izstrādātāju pieredze, lai uzzinātu, kā AGENTS.md dokumentē šo izkārtojumu kodēšanas asistentiem.

Pamata lietošana (Flight::path())

Pieņemsim, ka mums ir šāds direktoriju koks:

# Piemēra ceļš
/home/user/project/my-flight-project/
├── app
│   ├── cache
│   ├── config
│   ├── controllers - satur šī projekta kontrollerus
│   ├── translations
│   ├── UTILS - satur klases tikai šai lietojumprogrammai (tas ir ar lielajiem burtiem speciāli, lai vēlāk izmantotu kā piemēru)
│   └── views
└── public
    └── css
    └── js
    └── index.php

Jūs, iespējams, pamanījāt, ka tas ir līdzīgs tipiskam lietojumprogrammas kokam (pati dokumentācijas vietne izmanto strukturētu izkārtojumu). Mazie burti controllers šeit ir derīga izvēle—tas vienkārši nav skeletona pašreizējais noklusējums.

Jūs varat norādīt katru direktoriju, no kura ielādēt, šādi:


/**
 * public/index.php
 */

// Pievienojiet ceļu automātiskajai ielādei
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');


/**
 * app/controllers/MyController.php
 */

// nosaukumvieta nav nepieciešama

// Visām automātiski ielādētajām klasēm ieteicams izmantot Pascal Case (katrs vārds ar lielo sākumburtu, bez atstarpēm)
class MyController {

    public function index() {
        // dariet kaut ko
    }
}

Nosaukumvietas ar Flight::path()

Ja jums ir nosaukumvietas, to faktiski ir ļoti viegli ieviest. Jums vajadzētu izmantot Flight::path() metodi, lai norādītu lietojumprogrammas saknes direktoriju (nevis dokumenta sakni vai public/ mapi).


/**
 * public/index.php
 */

// Pievienojiet ceļu automātiskajai ielādei
Flight::path(__DIR__.'/../');

Tagad šādi varētu izskatīties jūsu kontrolieris. Apskatiet zemāk esošo piemēru, bet pievērsiet uzmanību komentāriem — tajos ir svarīga informācija.

/**
 * app/controllers/MyController.php
 */

// nosaukumvietas ir obligātas
// nosaukumvietas ir tādas pašas kā direktoriju struktūra
// nosaukumvietām ir jāievēro tāds pats burtu reģistrs kā direktoriju struktūrai
// nosaukumvietās un direktorijos nevar būt apakšsvītras (ja vien nav iestatīts Loader::setV2ClassLoading(false))
namespace app\controllers;

// Visām automātiski ielādētajām klasēm ieteicams izmantot Pascal Case (katrs vārds ar lielo sākumburtu, bez atstarpēm)
// Sākot ar 3.7.2, varat izmantot Pascal_Snake_Case savu klašu nosaukumos, izpildot Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // dariet kaut ko
    }
}

Un, ja vēlaties automātiski ielādēt klasi savā utils direktorijā, jūs darītu pamatā to pašu:


/**
 * app/UTILS/ArrayHelperUtil.php
 */

// nosaukumvietai ir jāatbilst direktoriju struktūrai un reģistram (ņemiet vērā, ka UTILS direktorijs ir ar lielajiem burtiem
//     tāpat kā iepriekš redzamajā failu kokā)
namespace app\UTILS;

class ArrayHelperUtil {

    public function changeArrayCase(array $array) {
        // dariet kaut ko
    }
}

Skeletona stila nosaukumvieta (tie paši noteikumi, cits reģistrs)

/**
 * app/Controller/MyController.php
 */
namespace App\Controller;

class MyController {
    // ...
}

Noteikumi nav mainījušies—tikai skeletona izvēlētais mapes/nosaukumvietas reģistrs. Neatkarīgi no tā, kādu reģistru izmantojat savās mapēs, jūsu namespace rindai ir jāatbilst.

Apakšsvītras klašu nosaukumos

Sākot ar 3.7.2, varat izmantot Pascal_Snake_Case savu klašu nosaukumos, izpildot Loader::setV2ClassLoading(false);. Tas ļaus jums izmantot apakšsvītras klašu nosaukumos. Tas nav ieteicams, bet ir pieejams tiem, kam tas ir nepieciešams.

use flight\core\Loader;

/**
 * public/index.php
 */

// Pievienojiet ceļu automātiskajai ielādei
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);

/**
 * app/controllers/My_Controller.php
 */

// nosaukumvieta nav nepieciešama

class My_Controller {

    public function index() {
        // dariet kaut ko
    }
}

Skatīt arī

Problēmu novēršana

Klase nav atrasta (automātiskā ielāde nedarbojas)

Var būt vairāki iemesli, kāpēc tas nenotiek. Zemāk ir daži piemēri.

Nepareizs faila nosaukums

Visizplatītākais ir tas, ka klases nosaukums neatbilst faila nosaukumam.

Ja jums ir klase ar nosaukumu MyClass, failam ir jābūt nosauktam MyClass.php. Ja jums ir klase ar nosaukumu MyClass un fails ir nosaukts myclass.php, automātiskā ielāde nevarēs to atrast.

Nepareiza nosaukumvieta vai mapes reģistrs

Ja izmantojat nosaukumvietas, tad nosaukumvietai ir jāatbilst direktoriju struktūrai ieskaitot reģistru.

// ...kods...

// ja jūsu MyController atrodas app/Controller (skeletons) un ir nosaukumvietā App\Controller
// tas nedarbosies:
Flight::route('/hello', 'MyController->hello');

// Skeletona stils:
use App\Controller\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);

// Vecāks izkārtojums ar mazajiem burtiem (tikai tad, ja jūsu mapes patiešām ir app/controllers):
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// vai pilnībā kvalificēts:
Flight::route('/hello', [ 'App\Controller\MyController', 'hello' ]);

path() nav definēts (ne-Composer lietojumprogrammas kods)

Ja paļaujaties uz Flight::path() nevis Composer lietojumprogrammas klasēm, definējiet ceļu pirms maršrutiem, kas atsaucas uz šīm klasēm (parasti agri bootstrap / public/index.php):

// Pievienojiet ceļu automātiskajai ielādei (projekta sakne nosaukumvietu lietotnēm)
Flight::path(__DIR__.'/../');

Oficiālais skeletons galvenokārt izmanto Composer PSR-4 App\ klasei, tāpēc parasti jums nebūs nepieciešams Flight::path() kontrolleriem un modeļiem.

Izmaiņu žurnāls

Learn/uploaded_file

Augšupielādētā faila apstrādātājs

Pārskats

UploadedFile klase Flight padara vieglu un drošu failu augšupielādes apstrādi jūsu lietojumprogrammā. Tā aptver PHP failu augšupielādes procesa detaļas, piedāvājot vienkāršu, objektorientētu veidu, kā piekļūt faila informācijai un pārvietot augšupielādētos failus.

Saprašana

Kad lietotājs augšupielādē failu caur formu, PHP saglabā informāciju par failu $_FILES superglobālajā mainīgajā. Flight vidē jūs reti mijiedarbojaties ar $_FILES tieši. Tā vietā Flight Request objekts (pieejams caur Flight::request()) nodrošina getUploadedFiles() metodi, kas atgriež UploadedFile objektu masīvu, padarot failu apstrādi daudz ērtāku un izturīgāku.

UploadedFile klase nodrošina metodes, lai:

Šī klase palīdz izvairīties no izplatītām kļūdām ar failu augšupielādi, piemēram, kļūdu apstrādes vai drošas failu pārvietošanas.

Pamata izmantošana

Piekļuve augšupielādētajiem failiem no pieprasījuma

Ieteicamais veids, kā piekļūt augšupielādētajiem failiem, ir caur pieprasījuma objektu:

Flight::route('POST /upload', function() {
    // Par formu lauku ar nosaukumu <input type="file" name="myFile">
    $uploadedFiles = Flight::request()->getUploadedFiles();
    $file = $uploadedFiles['myFile'];

    // Tagad jūs varat izmantot UploadedFile metodes
    if ($file->getError() === UPLOAD_ERR_OK) {
        $file->moveTo('/path/to/uploads/' . $file->getClientFilename());
        echo "Fails augšupielādēts veiksmīgi!";
    } else {
        echo "Augšupielāde neizdevās: " . $file->getError();
    }
});

Daudzu failu augšupielādes apstrāde

Ja jūsu forma izmanto name="myFiles[]" vairāku augšupielāžu gadījumā, jūs saņemsiet UploadedFile objektu masīvu:

Flight::route('POST /upload', function() {
    // Par formu lauku ar nosaukumu <input type="file" name="myFiles[]">
    $uploadedFiles = Flight::request()->getUploadedFiles();
    foreach ($uploadedFiles['myFiles'] as $file) {
        if ($file->getError() === UPLOAD_ERR_OK) {
            $file->moveTo('/path/to/uploads/' . $file->getClientFilename());
            echo "Augšupielādēts: " . $file->getClientFilename() . "<br>";
        } else {
            echo "Neizdevās augšupielādēt: " . $file->getClientFilename() . "<br>";
        }
    }
});

UploadedFile instances manuāla izveide

Parasti jūs neizveidosiet UploadedFile manuāli, bet to var izdarīt, ja nepieciešams:

use flight\net\UploadedFile;

$file = new UploadedFile(
  $_FILES['myfile']['name'],
  $_FILES['myfile']['type'],
  $_FILES['myfile']['size'],
  $_FILES['myfile']['tmp_name'],
  $_FILES['myfile']['error']
);

Piekļuve faila informācijai

Jūs viegli varat iegūt detaļas par augšupielādēto failu:

echo $file->getClientFilename();   // Oriģinālais faila nosaukums no lietotāja datora
echo $file->getClientMediaType();  // MIME tips (piem., image/png)
echo $file->getSize();             // Faila izmērs baitos
echo $file->getTempName();         // Pagaidu faila ceļš uz servera
echo $file->getError();            // Augšupielādes kļūdas kods (0 nozīmē bez kļūdas)

Augšupielādētā faila pārvietošana

Pēc faila validēšanas pārvietojiet to uz pastāvīgu atrašanās vietu:

try {
  $file->moveTo('/path/to/uploads/' . $file->getClientFilename());
  echo "Fails augšupielādēts veiksmīgi!";
} catch (Exception $e) {
  echo "Augšupielāde neizdevās: " . $e->getMessage();
}

moveTo() metode izmet izņēmumu, ja kaut kas noiet greizi (piemēram, augšupielādes kļūda vai atļauju problēma).

Augšupielādes kļūdu apstrāde

Ja augšupielādes laikā radās problēma, jūs varat iegūt lasāmu kļūdas ziņojumu cilvēkam:

if ($file->getError() !== UPLOAD_ERR_OK) {
  // Jūs varat izmantot kļūdas kodu vai noķert izņēmumu no moveTo()
  echo "Radās kļūda, augšupielādējot failu.";
}

Skatīt arī

Traucējummeklēšana

Izmaiņu žurnāls

Guides/unit_testing

Vienību testēšana Flight PHP ar PHPUnit

Šis ceļvedis iepazīstina ar vienību testēšanu Flight PHP, izmantojot PHPUnit, un ir paredzēts iesācējiem, kuri vēlas saprast, kāpēc vienību testēšanai ir nozīme un kā to praktiski pielietot. Mēs koncentrēsimies uz uzvedības testēšanu — nodrošinot, ka jūsu lietotne dara to, ko jūs sagaidāt, piemēram, nosūta e-pastu vai saglabā ierakstu — nevis uz triviāliem aprēķiniem. Sāksim ar vienkāršu maršruta apstrādātāju un pāriesim uz sarežģītāku kontrolleri, iekļaujot atkarību injekciju (DI) un trešo pušu pakalpojumu atdarināšanu.

Kāpēc vienību testēšana?

Vienību testēšana nodrošina, ka jūsu kods darbojas, kā paredzēts, atklājot kļūdas, pirms tās nonāk ražošanā. Tas ir īpaši vērtīgi Flight, kur vieglā maršrutēšana un elastība var novest pie sarežģītām mijiedarbībām. Atsevišķiem izstrādātājiem vai komandām vienību testi kalpo kā drošības tīkls, dokumentējot paredzamo uzvedību un novēršot regresijas, kad vēlāk atgriežaties pie sava koda. Tie arī uzlabo dizainu: grūti testējams kods bieži norāda uz pārāk sarežģītām vai cieši saistītām klasēm.

Atšķirībā no vienkāršotiem piemēriem (piemēram, testējot x * y = z), mēs koncentrēsimies uz reālās pasaules uzvedību, piemēram, ievades validāciju, datu saglabāšanu vai darbību aktivizēšanu, piemēram, e-pastus. Mūsu mērķis ir padarīt testēšanu pieejamu un jēgpilnu.

Vispārīgi vadošie principi

  1. Testējiet uzvedību, nevis ieviešanu: Koncentrējieties uz rezultātiem (piemēram, "e-pasts nosūtīts" vai "ieraksts saglabāts"), nevis uz iekšējām detaļām. Tas padara testus izturīgus pret refaktorēšanu.
  2. Pārtrauciet lietot Flight::: Flight statiskās metodes ir ļoti ērtas, bet apgrūtina testēšanu. Jums vajadzētu pierast lietot $app mainīgo no $app = Flight::app();. $app ir visas tās pašas metodes, kas ir Flight::. Jūs joprojām varēsiet lietot $app->route() vai $this->app->json() savā kontrollerī utt. Tāpat izmantojiet īsto Flight maršrutētāju ar $router = $app->router() un pēc tam varat lietot $router->get(), $router->post(), $router->group() utt. Skatiet Maršrutēšana.
  3. Turiet testus ātrus: Ātri testi veicina biežu izpildi. Izvairieties no lēnām darbībām, piemēram, datubāzes izsaukumiem vienību testos. Ja jums ir lēns tests, tā ir zīme, ka rakstāt integrācijas testu, nevis vienību testu. Integrācijas testi ir tad, kad faktiski iesaistāt reālas datubāzes, reālus HTTP izsaukumus, reālu e-pasta sūtīšanu utt. Tiem ir sava vieta, bet tie ir lēni un var būt nestabili, kas nozīmē, ka tie dažkārt neizdodas nezināma iemesla dēļ.
  4. Izmantojiet aprakstošus nosaukumus: Testu nosaukumiem skaidri jāapraksta pārbaudāmā uzvedība. Tas uzlabo lasāmību un uzturējamību.
  5. Izvairieties no globālajiem mainīgajiem kā no mēra: Samaziniet $app->set() un $app->get() lietošanu, jo tie darbojas kā globālais stāvoklis, prasot atdarinājumus katrā testā. Dodiet priekšroku DI vai DI konteineram (skatiet Atkarību injekcijas konteiners). Pat $app->map() metodes izmantošana tehniski ir "globāla", un no tās vajadzētu izvairīties par labu DI. Izmantojiet sesijas bibliotēku, piemēram, flightphp/session, lai testos varētu atdarināt sesijas objektu. Neizsauciet $_SESSION tieši savā kodā, jo tā ir globālā mainīgā ieviešana jūsu kodā, kas apgrūtina testēšanu.
  6. Izmantojiet atkarību injekciju: Injicējiet atkarības (piemēram, PDO, e-pasta sūtītājus) kontrolleros, lai izolētu loģiku un vienkāršotu atdarināšanu. Ja jums ir klase ar pārāk daudz atkarībām, apsveriet iespēju to refaktorēt mazākās klasēs, no kurām katra ir atbildīga par vienu lietu, ievērojot SOLID principus.
  7. Atdariniet trešo pušu pakalpojumus: Atdariniet datubāzes, HTTP klientus (cURL) vai e-pasta pakalpojumus, lai izvairītos no ārējiem izsaukumiem. Testējiet vienu vai divus slāņus dziļi, bet ļaujiet savai pamatloģikai darboties. Piemēram, ja jūsu lietotne sūta īsziņas, jūs NEVĒLATIES patiešām sūtīt īsziņu katru reizi, kad palaižat testus, jo šīs izmaksas uzkrāsies (un būs lēnāk). Tā vietā atdariniet īsziņu pakalpojumu un vienkārši pārbaudiet, vai jūsu kods izsauca īsziņu pakalpojumu ar pareizajiem parametriem.
  8. Tiecieties uz augstu pārklājumu, nevis pilnību: 100% rindu pārklājums ir labs, bet tas nenozīmē, ka viss jūsu kodā ir testēts tā, kā vajadzētu (izpētiet zaru/ceļu pārklājumu PHPUnit). Prioritāti piešķiriet kritiskajai uzvedībai (piemēram, lietotāja reģistrācijai, API atbildēm un neveiksmīgu atbilžu tveršanai).
  9. Izmantojiet kontrollerus maršrutiem: Maršrutu definīcijās izmantojiet kontrollerus, nevis slēgumus. flight\Engine $app pēc noklusējuma tiek injicēts katrā kontrollerī caur konstruktoru. Testos izmantojiet $app = new Flight\Engine(), lai testa ietvaros izveidotu Flight instanci, injicētu to savā kontrollerī un izsauktu metodes tieši (piemēram, $controller->register()). Skatiet Flight paplašināšana un Maršrutēšana.
  10. Izvēlieties atdarināšanas stilu un pieturieties pie tā: PHPUnit atbalsta vairākus atdarināšanas stilus (piemēram, prophecy, iebūvētos atdarinājumus), vai arī varat izmantot anonīmās klases, kurām ir savas priekšrocības, piemēram, koda pabeigšana, lūšana, ja maināt metodes definīciju, utt. Vienkārši esiet konsekventi savos testos. Skatiet PHPUnit Mock Objects.
  11. Izmantojiet protected redzamību metodēm/īpašībām, kuras vēlaties testēt apakšklasēs: Tas ļauj tās pārdefinēt testa apakšklasēs, nepadarot tās publiskas; tas ir īpaši noderīgi anonīmu klašu atdarinājumiem.

PHPUnit iestatīšana

Pirmkārt, iestatiet PHPUnit savā Flight PHP projektā, izmantojot Composer, lai atvieglotu testēšanu. Skatiet PHPUnit sākšanas rokasgrāmatu, lai iegūtu vairāk informācijas.

  1. Sava projekta direktorijā izpildiet:

    composer require --dev phpunit/phpunit

    Tas instalē jaunāko PHPUnit kā izstrādes atkarību.

  2. Izveidojiet tests direktoriju sava projekta saknē testa failiem.

  3. Pievienojiet testa skriptu failam composer.json ērtībai:

    // cits composer.json saturs
    "scripts": {
        "test": "phpunit --configuration phpunit.xml"
    }
  4. Izveidojiet failu phpunit.xml saknē:

    <?xml version="1.0" encoding="UTF-8"?>
    <phpunit bootstrap="vendor/autoload.php">
        <testsuites>
            <testsuite name="Flight Tests">
                <directory>tests</directory>
            </testsuite>
        </testsuites>
    </phpunit>

Tagad, kad jūsu testi ir izveidoti, varat palaist composer test, lai izpildītu testus.

Vienkārša maršruta apstrādātāja testēšana

Sāksim ar pamata maršrutu, kas validē lietotāja e-pasta ievadi. Mēs testēsim tā uzvedību: veiksmes ziņojuma atgriešanu derīgiem e-pastiem un kļūdas ziņojumu nederīgiem. E-pasta validācijai mēs izmantojam filter_var.

// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);

// UserController.php
class UserController {
    protected $app;

    public function __construct(flight\Engine $app) {
        $this->app = $app;
    }

    public function register() {
        $email = $this->app->request()->data->email;
        $responseArray = [];
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $responseArray = ['status' => 'error', 'message' => 'Invalid email'];
        } else {
            $responseArray = ['status' => 'success', 'message' => 'Valid email'];
        }

        $this->app->json($responseArray);
    }
}

Lai to testētu, izveidojiet testa failu. Skatiet Vienību testēšana un SOLID principi, lai uzzinātu vairāk par testu strukturēšanu:

// tests/UserControllerTest.php
use PHPUnit\Framework\TestCase;
use Flight;
use flight\Engine;

class UserControllerTest extends TestCase {

    public function testValidEmailReturnsSuccess() {
        $app = new Engine();
        $request = $app->request();
        $request->data->email = 'test@example.com'; // Simulē POST datus
        $UserController = new UserController($app);
        $UserController->register($request->data->email);
        $response = $app->response()->getBody();
        $output = json_decode($response, true);
        $this->assertEquals('success', $output['status']);
        $this->assertEquals('Valid email', $output['message']);
    }

    public function testInvalidEmailReturnsError() {
        $app = new Engine();
        $request = $app->request();
        $request->data->email = 'invalid-email'; // Simulē POST datus
        $UserController = new UserController($app);
        $UserController->register($request->data->email);
        $response = $app->response()->getBody();
        $output = json_decode($response, true);
        $this->assertEquals('error', $output['status']);
        $this->assertEquals('Invalid email', $output['message']);
    }
}

Galvenie punkti:

Izpildiet composer test, lai pārliecinātos, ka maršruts darbojas, kā paredzēts. Lai uzzinātu vairāk par pieprasījumiem un atbildēm Flight, skatiet attiecīgo dokumentāciju.

Atkarību injekcijas izmantošana testējamiem kontrolleriem

Sarežģītākiem scenārijiem izmantojiet atkarību injekciju (DI), lai kontrollerus padarītu testējamus. Izvairieties no Flight globālajiem mainīgajiem (piemēram, Flight::set(), Flight::map(), Flight::register()), jo tie darbojas kā globālais stāvoklis, prasot atdarinājumus katrā testā. Tā vietā izmantojiet Flight DI konteineru, DICE, PHP-DI vai manuālo DI.

Izmantosim flight\database\SimplePdo, nevis tiešu PDO. Šo palīgu ir daudz vieglāk atdarināt un vienību testēt (un tas ir vēlamāks par novecojušo PdoWrapper).

Šeit ir kontrolleris, kas saglabā lietotāju datubāzē un nosūta sagaidīšanas e-pastu:

use flight\database\SimplePdo;

class UserController {
    protected $app;
    protected $db;
    protected $mailer;

    public function __construct(Engine $app, SimplePdo $db, MailerInterface $mailer) {
        $this->app = $app;
        $this->db = $db;
        $this->mailer = $mailer;
    }

    public function register() {
        $email = $this->app->request()->data->email;
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            // return pievienošana šeit palīdz apturēt izpildi vienību testēšanā
            return $this->app->jsonHalt(['status' => 'error', 'message' => 'Invalid email']);
        }

        $this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
        $this->mailer->sendWelcome($email);

        return $this->app->json(['status' => 'success', 'message' => 'User registered']);
    }
}

Galvenie punkti:

Kontrollera testēšana ar atdarinājumiem (mocks)

Tagad testēsim UserController uzvedību: e-pastu validāciju, saglabāšanu datubāzē un e-pastu sūtīšanu. Mēs atdarināsim datubāzi un e-pasta sūtītāju, lai izolētu kontrolleri.

// tests/UserControllerDICTest.php
use flight\database\SimplePdo;
use PHPUnit\Framework\TestCase;

class UserControllerDICTest extends TestCase {
    public function testValidEmailSavesAndSendsEmail() {

        // Dažreiz ir nepieciešams sajaukt atdarināšanas stilus
        // Šeit mēs izmantojam PHPUnit iebūvēto atdarinājumu PDOStatement
        $statementMock = $this->createMock(PDOStatement::class);
        $statementMock->method('execute')->willReturn(true);
        // Anonīmas klases izmantošana, lai atdarinātu SimplePdo
        $mockDb = new class($statementMock) extends SimplePdo {
            protected $statementMock;
            public function __construct($statementMock) {
                $this->statementMock = $statementMock;
            }

            // Kad mēs to atdarinām šādi, mēs īsti neveicam datubāzes izsaukumu.
            // Mēs varam to tālāk konfigurēt, lai mainītu PDOStatement atdarinājumu, simulējot kļūmes utt.
            public function runQuery(string $sql, array $params = []): PDOStatement {
                return $this->statementMock;
            }
        };
        $mockMailer = new class implements MailerInterface {
            public $sentEmail = null;
            public function sendWelcome($email): bool {
                $this->sentEmail = $email;
                return true;    
            }
        };
        $app = new Engine();
        $app->request()->data->email = 'test@example.com';
        $controller = new UserControllerDIC($app, $mockDb, $mockMailer);
        $controller->register();
        $response = $app->response()->getBody();
        $result = json_decode($response, true);
        $this->assertEquals('success', $result['status']);
        $this->assertEquals('User registered', $result['message']);
        $this->assertEquals('test@example.com', $mockMailer->sentEmail);
    }

    public function testInvalidEmailSkipsSaveAndEmail() {
         $mockDb = new class() extends SimplePdo {
            // Tukšs konstruktors apiet vecāka konstruktoru
            public function __construct() {}
            public function runQuery(string $sql, array $params = []): PDOStatement {
                throw new Exception('Should not be called');
            }
        };
        $mockMailer = new class implements MailerInterface {
            public $sentEmail = null;
            public function sendWelcome($email): bool {
                throw new Exception('Should not be called');
            }
        };
        $app = new Engine();
        $app->request()->data->email = 'invalid-email';

        // Nepieciešams kartēt jsonHalt, lai izvairītos no izejas
        $app->map('jsonHalt', function($data) use ($app) {
            $app->json($data, 400);
        });
        $controller = new UserControllerDIC($app, $mockDb, $mockMailer);
        $controller->register();
        $response = $app->response()->getBody();
        $result = json_decode($response, true);
        $this->assertEquals('error', $result['status']);
        $this->assertEquals('Invalid email', $result['message']);
    }
}

Galvenie punkti:

Pārāk liela atdarināšana

Esiet uzmanīgi, lai neatdarinātu pārāk lielu daļu sava koda. Tālāk es sniegšu piemēru, kāpēc tas varētu būt slikti, izmantojot mūsu UserController. Mēs mainīsim šo pārbaudi uz metodi ar nosaukumu isEmailValid (izmantojot filter_var), bet pārējos jaunos papildinājumus — uz atsevišķu metodi registerUser.

use flight\database\SimplePdo;
use flight\Engine;

// UserControllerDICV2.php
class UserControllerDICV2 {
    protected $app;
    protected $db;
    protected $mailer;

    public function __construct(Engine $app, SimplePdo $db, MailerInterface $mailer) {
        $this->app = $app;
        $this->db = $db;
        $this->mailer = $mailer;
    }

    public function register() {
        $email = $this->app->request()->data->email;
        if (!$this->isEmailValid($email)) {
            // return pievienošana šeit palīdz apturēt izpildi vienību testēšanā
            return $this->app->jsonHalt(['status' => 'error', 'message' => 'Invalid email']);
        }

        $this->registerUser($email);

        $this->app->json(['status' => 'success', 'message' => 'User registered']);
    }

    protected function isEmailValid($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }

    protected function registerUser($email) {
        $this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
        $this->mailer->sendWelcome($email);
    }
}

Un tagad pārāk atdarinātais vienību tests, kas īsti neko netesta:

use PHPUnit\Framework\TestCase;

class UserControllerTest extends TestCase {
    public function testValidEmailSavesAndSendsEmail() {
        $app = new Engine();
        $app->request()->data->email = 'test@example.com';
        // mēs šeit izlaižam papildu atkarību injekciju, jo tas ir "viegli"
        $controller = new class($app) extends UserControllerDICV2 {
            protected $app;
            // Apiet atkarības konstruktorā
            public function __construct($app) {
                $this->app = $app;
            }

            // Mēs vienkārši piespiedīsim, lai tas būtu derīgs.
            protected function isEmailValid($email) {
                return true; // Vienmēr atgriež true, apejot reālo validāciju
            }

            // Apiet faktiskos datubāzes un e-pasta sūtītāja izsaukumus
            protected function registerUser($email) {
                return false;
            }
        };
        $controller->register();
        $response = $app->response()->getBody();
        $result = json_decode($response, true);
        $this->assertEquals('success', $result['status']);
        $this->assertEquals('User registered', $result['message']);
    }
}

Urrā, mums ir vienību testi un tie izdodas! Bet pagaidiet, kas notiktu, ja es faktiski mainītu isEmailValid vai registerUser iekšējo darbību? Mani testi joprojām izdotos, jo es esmu atdarinājis visu funkcionalitāti. Ļaujiet man parādīt, ko es domāju.

// UserControllerDICV2.php
class UserControllerDICV2 {

    // ... citas metodes ...

    protected function isEmailValid($email) {
        // Mainītā loģika
        $validEmail = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
        // Tagad tam vajadzētu būt tikai noteiktam domēnam
        $validDomain = strpos($email, '@example.com') !== false; 
        return $validEmail && $validDomain;
    }
}

Ja es palaistu savus iepriekš minētos vienību testus, tie joprojām izdotos! Bet, tā kā es netestēju uzvedību (faktiski neļaujot daļai koda izpildīties), es, iespējams, esmu ieprogrammējis kļūdu, kas gaida, lai notiktu ražošanā. Tests būtu jāmaina, lai ņemtu vērā jauno uzvedību, kā arī pretējo gadījumu, kad uzvedība nav tāda, kādu mēs sagaidām.

Pilns piemērs

Pilnu Flight PHP projekta piemēru ar vienību testiem varat atrast GitHub: n0nag0n/flight-unit-tests-guide. Lai iegūtu dziļāku izpratni, skatiet Vienību testēšana un SOLID principi.

Biežākās kļūdas

Mērogošana ar vienību testiem

Vienību testi noder lielākos projektos vai tad, kad pēc mēnešiem atgriežaties pie koda. Tie dokumentē uzvedību un atklāj regresijas, glābjot jūs no lietotnes no jauna apgūšanas. Atsevišķiem izstrādātājiem testējiet kritiskos ceļus (piemēram, lietotāja reģistrāciju, maksājumu apstrādi). Komandām testi nodrošina konsekventu uzvedību visās izmaiņās. Skatiet Kāpēc ietvari?, lai uzzinātu vairāk par ieguvumiem, ko sniedz ietvari un testi.

Dalieties ar saviem testēšanas padomiem Flight PHP dokumentācijas krātuvē!

Rakstījis n0nag0n 2025

Guides/blog

Vienkārša emuāra izveide ar Flight PHP

Šis ceļvedis palīdzēs jums izveidot vienkāršu emuāru, izmantojot Flight PHP ietvaru. Jūs izveidosiet projektu, definēsiet maršrutus, pārvaldīsiet ierakstus ar JSON un atveidosiet tos ar Latte šablonu dzinēju—vienlaikus parādot Flight vienkāršību un elastīgumu. Līdz beigām jums būs funkcionējošs emuārs ar sākumlapu, atsevišķu ierakstu lapām un izveides formu.

Priekšnosacījumi

1. solis: Projekta iestatīšana

Sāciet, izveidojot jaunu projekta direktoriju un instalējot Flight, izmantojot Composer.

  1. Izveidojiet direktoriju:

    mkdir flight-blog
    cd flight-blog
  2. Instalējiet Flight:

    composer require flightphp/core
  3. Izveidojiet publisko direktoriju: Flight izmanto vienu ieejas punktu (index.php). Izveidojiet tam public/ mapi:

    mkdir public
  4. Vienkāršs index.php: Izveidojiet public/index.php ar vienkāršu “hello world” maršrutu:

    <?php
    require '../vendor/autoload.php';
    
    Flight::route('/', function () {
        echo 'Hello, Flight!';
    });
    
    Flight::start();
  5. Palaidiet iebūvēto serveri: Pārbaudiet savu iestatījumu ar PHP izstrādes serveri:

    php -S localhost:8000 -t public/

    Apmeklējiet http://localhost:8000, lai redzētu “Hello, Flight!”.

2. solis: Projekta struktūras organizēšana

Lai iegūtu tīru iestatījumu, strukturējiet projektu šādi:

flight-blog/
├── app/
│   ├── config/
│   └── views/
├── data/
├── public/
│   └── index.php
├── vendor/
└── composer.json

3. solis: Latte instalēšana un konfigurēšana

Latte ir viegls šablonu dzinējs, kas labi integrējas ar Flight.

  1. Instalējiet Latte:

    composer require latte/latte
  2. Konfigurējiet Latte Flight: Atjauniniet public/index.php, lai reģistrētu Latte kā skatu dzinēju:

    <?php
    require '../vendor/autoload.php';
    
    use Latte\Engine;
    
    Flight::register('view', Engine::class, [], function ($latte) {
        $latte->setTempDirectory(__DIR__ . '/../cache/');
        $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/'));
    });
    
    Flight::route('/', function () {
        Flight::view()->render('home.latte', ['title' => 'My Blog']);
    });
    
    Flight::start();
  3. Izveidojiet izkārtojuma veidni: Failā app/views/layout.latte:

    <!DOCTYPE html>
    <html>
    <head>
        <title>{$title}</title>
    </head>
    <body>
        <header>
            <h1>My Blog</h1>
            <nav>
                <a href="/">Home</a> | 
                <a href="/create">Create a Post</a>
            </nav>
        </header>
        <main>
            {block content}{/block}
        </main>
        <footer>
            <p>&copy; {date('Y')} Flight Blog</p>
        </footer>
    </body>
    </html>
  4. Izveidojiet sākumlapas veidni: Failā app/views/home.latte:

    {extends 'layout.latte'}
    
     {block content}
         <h2>{$title}</h2>
         <ul>
         {foreach $posts as $post}
             <li><a href="/post/{$post['slug']}">{$post['title']}</a></li>
         {/foreach}
         </ul>
     {/block}

    Restartējiet serveri, ja esat no tā izgājis, un apmeklējiet http://localhost:8000, lai redzētu atveidoto lapu.

  5. Izveidojiet datu failu: Izmantojiet JSON failu, lai vienkāršības labad simulētu datubāzi. Failā data/posts.json:

    [
        {
            "slug": "first-post",
            "title": "My First Post",
            "content": "This is my very first blog post with Flight PHP!"
        }
    ]

4. solis: Maršrutu definēšana

Atdaliet savus maršrutus konfigurācijas failā, lai nodrošinātu labāku organizāciju.

  1. Izveidojiet routes.php: Failā app/config/routes.php:

    <?php
    Flight::route('/', function () {
        Flight::view()->render('home.latte', ['title' => 'My Blog']);
    });
    
    Flight::route('/post/@slug', function ($slug) {
        Flight::view()->render('post.latte', ['title' => 'Post: ' . $slug, 'slug' => $slug]);
    });
    
    Flight::route('GET /create', function () {
        Flight::view()->render('create.latte', ['title' => 'Create a Post']);
    });
  2. Atjauniniet index.php: Iekļaujiet maršrutu failu:

    <?php
    require '../vendor/autoload.php';
    
    use Latte\Engine;
    
    Flight::register('view', Engine::class, [], function ($latte) {
        $latte->setTempDirectory(__DIR__ . '/../cache/');
        $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/'));
    });
    
    require '../app/config/routes.php';
    
    Flight::start();

5. solis: Emuāra ierakstu glabāšana un ielāde

Pievienojiet metodes ierakstu ielādei un saglabāšanai.

  1. Pievienojiet ierakstu metodi: Failā index.php pievienojiet metodi ierakstu ielādei:

    Flight::map('posts', function () {
        $file = __DIR__ . '/../data/posts.json';
        return json_decode(file_get_contents($file), true);
    });
  2. Atjauniniet maršrutus: Modificējiet app/config/routes.php, lai izmantotu ierakstus:

    <?php
    Flight::route('/', function () {
        $posts = Flight::posts();
        Flight::view()->render('home.latte', [
            'title' => 'My Blog',
            'posts' => $posts
        ]);
    });
    
    Flight::route('/post/@slug', function ($slug) {
        $posts = Flight::posts();
        $post = array_filter($posts, fn($p) => $p['slug'] === $slug);
        $post = reset($post) ?: null;
        if (!$post) {
            Flight::notFound();
            return;
        }
        Flight::view()->render('post.latte', [
            'title' => $post['title'],
            'post' => $post
        ]);
    });
    
    Flight::route('GET /create', function () {
        Flight::view()->render('create.latte', ['title' => 'Create a Post']);
    });

6. solis: Veidņu izveide

Atjauniniet savas veidnes, lai attēlotu ierakstus.

  1. Ieraksta lapa (app/views/post.latte):

    {extends 'layout.latte'}
    
     {block content}
         <h2>{$post['title']}</h2>
         <div class="post-content">
             <p>{$post['content']}</p>
         </div>
     {/block}

7. solis: Ierakstu izveides pievienošana

Apstrādājiet veidlapas iesniegšanu, lai pievienotu jaunus ierakstus.

  1. Izveidojiet veidlapu (app/views/create.latte):

    {extends 'layout.latte'}
    
     {block content}
         <h2>{$title}</h2>
         <form method="POST" action="/create">
             <div class="form-group">
                 <label for="title">Title:</label>
                 <input type="text" name="title" id="title" required>
             </div>
             <div class="form-group">
                 <label for="content">Content:</label>
                 <textarea name="content" id="content" required></textarea>
             </div>
             <button type="submit">Save Post</button>
         </form>
     {/block}
  2. Pievienojiet POST maršrutu: Failā app/config/routes.php:

    Flight::route('POST /create', function () {
        $request = Flight::request();
        $title = $request->data['title'];
        $content = $request->data['content'];
        $slug = strtolower(str_replace(' ', '-', $title));
    
        $posts = Flight::posts();
        $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content];
        file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT));
    
        Flight::redirect('/');
    });
  3. Izmēģiniet to:

    • Apmeklējiet http://localhost:8000/create.
    • Iesniedziet jaunu ierakstu (piemēram, “Otrais ieraksts” ar kādu saturu).
    • Pārbaudiet sākumlapu, lai redzētu to sarakstā.

8. solis: Kļūdu apstrādes uzlabošana

Pārdefinējiet notFound metodi, lai nodrošinātu labāku 404 pieredzi.

Failā index.php:

Flight::map('notFound', function () {
    Flight::view()->render('404.latte', ['title' => 'Page Not Found']);
});

Izveidojiet app/views/404.latte:

{extends 'layout.latte'}

{block content}
    <h2>404 - {$title}</h2>
    <p>Sorry, that page doesn't exist!</p>
{/block}

Turpmākās darbības

Secinājums

Jūs esat izveidojuši vienkāršu emuāru ar Flight PHP! Šis ceļvedis demonstrē galvenās funkcijas, piemēram, maršrutēšanu, šablonu izmantošanu ar Latte un veidlapu iesniegumu apstrādi—vienlaikus saglabājot vieglumu. Izpētiet Flight dokumentāciju, lai uzzinātu par papildu funkcijām, kas palīdzēs attīstīt jūsu emuāru tālāk!

License

MIT licences (MIT)

=====================

Autortiesības © 2024 @mikecao, @n0nag0n

Atļauja tiek piešķirta bez maksas jebkuram personai, kas iegūst šīs programmatūras kopiju un saistītos dokumentus (turpmāk - "Programmatūra"), izmantot Programmatūru jebkurā veidā bez ierobežojumiem, ieskaitot tiesības izmantot, kopēt, modificēt, apvienot, publicēt, izplatīt, licencēt un/vai pārdot Programmatūras kopijas, un atļaut personas, kam Programmatūra tiek nodrošināta to darīt, pakļautas zemāk minētajām nosacījumiem:

Minētās autortiesību paziņojums un šī atļaujas paziņojums ir jāiekļauj visās kopijās vai būtiskajos Programmatūras daļās.

PROGRAMMATŪRA TIEK NODROŠINĀTA "KĀ TĀDA", BEZ JEBKĀDAS GARANTIJAS IZTEIKTAS VAI IMPLICITAS, IETVEROT, BET NEIEROBEŽOTI, MERCHANTĀLĀS KVALITĀTES UN PIEMĒROTĪBAS KONKRĒTIEM MĒRĶIEM UN NEPĀRKĀPJUMS. NEVIENĀ GADĪJUMĀ AUTORI VAI AUTORTIESĪBU IEDZĪVOTĀJI NAV ATBILDĪGI PAR JEBKURU PRASĪBU, KAITĒJUMU VAI CITĀM SAISTĪBĀM, NEATKARĪGI NO TĀ, VAI TĀS IERAKSTS, IAUCIENS VAI CITĀDI RADIES SAISTĪBĀ AR PROGRAMMATŪRU VAI LIETOŠANU VAI CITĀDĀM DARĪBĀM AR PROGRAMMATŪRU.

About

Flight PHP Framework

Flight ir ātrs, vienkāršs, paplašināms PHP ietvars — izveidots izstrādātājiem, kuri vēlas ātri paveikt darbu bez liekām grūtībām. Neatkarīgi no tā, vai veidojat klasisku tīmekļa lietotni, zibenīgi ātru API vai strādājat kopā ar mākslīgā intelekta kodēšanas palīgiem, Flight nelielais apjoms un vienkāršais dizains padara to par ideālu izvēli. Flight ir veidots kā viegls, bet var arī apmierināt uzņēmuma līmeņa arhitektūras prasības.

Kāpēc Izvēlēties Flight?

Video Pārskats

Vienkārši, vai ne?
Uzziniet vairāk par Flight dokumentācijā!

Ātrais Starts

Lai veiktu ātru minimālu instalāciju, instalējiet to ar Composer:

composer require flightphp/core

Vai arī varat lejupielādēt repozitorija zip failu šeit. Tad jums būs pamata index.php fails kā tālāk:

<?php

// if installed with composer
require 'vendor/autoload.php';
// or if installed manually by zip file
// require 'flight/Flight.php';

Flight::route('/', function() {
  echo 'hello world!';
});

Flight::route('/json', function() {
  Flight::json([
    'hello' => 'world'
  ]);
});

Flight::start();

Tas ir viss! Jums ir pamata Flight lietotne. Tagad varat palaist šo failu ar php -S localhost:8000 un apmeklēt http://localhost:8000 savā pārlūkprogrammā, lai redzētu izvadi.

Īsi Flight:: piemēri kā šis ir lieliski mācībām un mikro lietotnēm. Pilnīgam projekta izkārtojumam, ko dalās cilvēki un AI rīki, izmantojiet tālāk minēto skeletu.

Skelets/Boilerplate Lietotne

Ir oficiāls starteris, kas palīdz sākt jebkuru jaunu Flight projektu. Tas izveido struktūru, konfigurāciju, Composer skriptus un AI draudzīgas instrukcijas no paša sākuma.

Apskatiet flightphp/skeleton, lai iegūtu gatavu projektu, vai apmeklējiet piemērus lapu, lai gūtu iedvesmu. Vēlaties AI darba plūsmas detaļas? Izpētiet AI un izstrādātāja pieredzi.

Ko jūs iegūstat (augsta līmeņa):

Skelet Lietotnes Instalēšana

Pietiekami vienkārši!

# Create the new project
composer create-project flightphp/skeleton my-project/
# Enter your new project directory
cd my-project/
# Bring up the local dev-server to get started right away!
composer start

Tas izveido projekta struktūru, kopē config_sample.phpconfig.php (un .env.example.env, ja tāds ir), un jūs esat gatavs sākt. Izvēles parauga dati:

php runway migrate
# then visit /posts and /api/posts

Augsta Veiktspēja

Flight ir viens no ātrākajiem PHP ietvariem. Tā vieglais kodols nozīmē mazāku pieskaitāmību un lielāku ātrumu — ideāli gan tradicionālām lietotnēm, gan modernām AI atbalstītām darba plūsmām. Visus etalonus varat redzēt TechEmpower

Skatiet etalonu zemāk ar dažiem citiem populāriem PHP ietvariem.

Framework Plaintext Reqs/sec JSON Reqs/sec
Flight 190,421 182,491
Yii 145,749 131,434
Fat-Free 139,238 133,952
Slim 89,588 87,348
Phalcon 95,911 87,675
Symfony 65,053 63,237
Lumen 40,572 39,700
Laravel 26,657 26,901
CodeIgniter 20,628 19,901

Flight un AI

Vai esat ziņkārīgs, kā Flight sadarbojas ar kodēšanas LLM? Atklājiet, kā AGENTS.md, Runway ai:* komandas un skeleta izkārtojums uztur palīgus uz pareizā ceļa.

Stabilitāte un Atpakaļsavietojamība

Mēs novērtējam jūsu laiku. Mēs visi esam redzējuši ietvarus, kas pilnībā pārvērtē sevi ik pēc pāris gadiem, atstājot izstrādātājus ar salauztu kodu un dārgām migrācijām. Flight ir citāds. Flight v3 tika izstrādāts kā v2 papildinājums, kas nozīmē, ka API, ko jūs zināt un mīlat, nav noņemts. Patiesībā lielākā daļa v2 projektu darbosies bez jebkādām izmaiņām v3.

Mēs esam apņēmušies uzturēt Flight stabilu, lai jūs varētu koncentrēties uz savas lietotnes izveidi, nevis sava ietvara labošanu. Skelets var būt uzskatu balstīts jauniem projektiem; kodola API paliek pazīstami visiem citiem.

Kopiena

Mēs esam Matrix Chat

Matrix

Un Discord

Ieguldījums

Ir divi veidi, kā varat ieguldīt Flight:

  1. Ieguldīt kodola ietvarā, apmeklējot kodola repozitoriju.
  2. Palīdzēt padarīt dokumentāciju labāku! Šī dokumentācijas vietne tiek mitināta Github. Ja pamanāt kļūdu vai vēlaties kaut ko uzlabot, droši iesniedziet pull request. Mēs mīlam atjauninājumus un jaunas idejas — īpaši par AI un jaunām tehnoloģijām!

Prasības

Flight prasa PHP 7.4 vai jaunāku versiju.

Piezīme: PHP 7.4 tiek atbalstīts, jo pašreizējā rakstīšanas laikā (2024) PHP 7.4 ir noklusējuma versija dažām LTS Linux distribūcijām. Piespiežot pāriet uz PHP >8, radītu daudz problēmu šiem lietotājiem. Ietvars arī atbalsta PHP >8.

Licence

Flight tiek izlaists saskaņā ar MIT licenci.

Awesome-plugins/php_cookie

Sīkfaili

overclokk/cookie ir vienkārša bibliotēka sīkfailu pārvaldībai jūsu lietotnē.

Instalēšana

Instalēšana ir vienkārša ar komponistu.

composer require overclokk/cookie

Izmantos̄ana

Lietošana ir tik vienkārša kā jaunas metodes reģistrēšana Flight klases.


use Overclokk\Cookie\Cookie;

/*
 * Iestatiet savā palaišanas vai public/index.php failā
 */

Flight::register('cookie', Cookie::class);

/**
 * ExampleController.php
 */

class ExampleController {
    public function login() {
        // Iestatiet sīkfailu

        // jums vajadzētu, lai tas būtu false, tādēļ, lai jūs saņemtu jaunu instanci
        // izmantojiet zemāk esošo komentāru, ja vēlaties autovērstjanu
        /** @var \Overclokk\Cookie\Cookie $cookie */
        $cookie = Flight::cookie(false);
        $cookie->set(
            'stay_logged_in', // sīkfaila nosaukums
            '1', // vērtība, kuru vēlaties iestatīt
            86400, // cik sekundes sīkfailam būtu jāpastāv
            '/', // ceļš, kurā būs pieejams sīkfails
            'example.com', // domēns, kurā būs pieejams sīkfails
            true, // sīkfails tiks pārraidīts tikai pār šifrētu HTTPS savienojumu
            true // sīkfails būs pieejams tikai caur HTTP protokolu
        );

        // pēc izvēles, ja vēlaties saglabāt noklusējuma vērtības
        // un ātri iestatīt sīkfailu ilgu laiku
        $cookie->forever('stay_logged_in', '1');
    }

    public function home() {
        // Pārbaudiet, vai jums ir sīkfails
        if (Flight::cookie()->has('stay_logged_in')) {
            // ielieciet tos piemēram, informācijas panelī.
            Flight::redirect('/dashboard');
        }
    }
}

Awesome-plugins/php_encryption

PHP Šifrēšana

defuse/php-encryption ir bibliotēka, kas var tikt izmantota datu šifrēšanai un atšifrēšanai. Uzsākšana ir diezgan vienkārša, lai sāktu šifrēt un atšifrēt datus. Viņiem ir lielisks rokasgrāmata, kas palīdz izskaidrot pamatus par to, kā izmantot bibliotēku, kā arī svarīgu drošības aspektu, kas saistīti ar šifrēšanu.

Instalēšana

Instalēšana ir vienkārša ar komponistu.

composer require defuse/php-encryption

Iestatījumi

Pēc tam jums būs jāģenerē šifrēšanas atslēga.

vendor/bin/generate-defuse-key

Tas izvadīs atslēgu, ko būsiet jāsargā. Jūs varētu saglabāt atslēgu savā app/config/config.php failā masīvā faila apakšdaļā. Lai gan tas nav ideāla vieta, tas vismaz ir kaut kas.

Lietošana

Tagad, kad jums ir bibliotēka un šifrēšanas atslēga, varat sākt šifrēt un atšifrēt datus.


use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

/*
 * Uzstādiet savā bootstrap vai public/index.php failā
 */

// Šifrēšanas metode
Flight::map('encrypt', function($sastāvs_dati) {
    $šifrēšanas_atslēga = /* $config['encryption_key'] vai file_get_contents no vietas, kur likāt atslēgu */;
    return Crypto::encrypt($sastāvs_dati, Key::loadFromAsciiSafeString($šifrēšanas_atslēga));
});

// Atšifrēšanas metode
Flight::map('decrypt', function($šifrētie_dati) {
    $šifrēšanas_atslēga = /* $config['encryption_key'] vai file_get_contents no vietas, kur likāt atslēgu */;
    try {
        $sastāvs_dati = Crypto::decrypt($šifrētie_dati, Key::loadFromAsciiSafeString($šifrēšanas_atslēga));
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
        // Uzbrukums! Vai nu tika ielādēta nepareizā atslēga, vai arī šifrētais teksts ir mainījies, kopš tas tika izveidots - vai nu sabojājies datu bāzē vai nodomāti modificējis Eva, mēģinot veikt uzbrukumu.

        // ... apstrādājiet šo gadījumu tādā veidā, kas ir piemērots jūsu lietojumprogrammai ...
    }
    return $sastāvs_dati;
});

Flight::route('/encrypt', function() {
    $šifrētie_dati = Flight::encrypt('Šis ir noslēpums');
    echo $šifrētie_dati;
});

Flight::route('/decrypt', function() {
    $šifrētie_dati = '...'; // Iegūstiet šifrētos datus no kaut kurienes
    $atšifrētie_dati = Flight::decrypt($šifrētie_dati);
    echo $atšifrētie_dati;
});

Awesome-plugins/php_file_cache

flightphp/cache

Viegls, vienkāršs un patstāvīgs PHP failu kešatmiņas klase, kas atdalīta no Wruczek/PHP-File-Cache

Priekšrocības

Šī dokumentācijas vietne izmanto šo bibliotēku, lai kešotu katru lapu!

Noklikšķiniet šeit, lai skatītu kodu.

Instalācija

Instalējiet, izmantojot composer:

composer require flightphp/cache

Izmantošana

Izmantošana ir diezgan vienkārša. Tas saglabā kešatmiņas failu kešatmiņas direktorijā.

use flight\Cache;

$app = Flight::app();

// Jūs nododat direktoriju, kurā tiks saglabāta kešatmiņa, konstruktorā
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {

    // Tas nodrošina, ka kešatmiņa tiek izmantota tikai ražošanas režīmā
    // ENVIRONMENT ir konstante, kas tiek iestatīta jūsu bootstrap failā vai citur jūsu lietotnē
    $cache->setDevMode(ENVIRONMENT === 'development');
});

Iegūt kešatmiņas vērtību

Jūs izmantojat metodi get(), lai iegūtu kešotu vērtību. Ja vēlaties ērtu metodi, kas atsvaidzinās kešatmiņu, ja tā ir beigusies, varat izmantot refreshIfExpired().


// Iegūt kešatmiņas instanci
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
    return date("H:i:s"); // atgriezt datus, kas jākešo
}, 10); // 10 sekundes

// vai
$data = $cache->get('simple-cache-test');
if(empty($data)) {
    $data = date("H:i:s");
    $cache->set('simple-cache-test', $data, 10); // 10 sekundes
}

Saglabāt kešatmiņas vērtību

Jūs izmantojat metodi set(), lai saglabātu vērtību kešatmiņā.

Flight::cache()->set('simple-cache-test', 'my cached data', 10); // 10 sekundes

Dzēst kešatmiņas vērtību

Jūs izmantojat metodi delete(), lai dzēstu vērtību kešatmiņā.

Flight::cache()->delete('simple-cache-test');

Pārbaudīt, vai kešatmiņas vērtība pastāv

Jūs izmantojat metodi exists(), lai pārbaudītu, vai vērtība pastāv kešatmiņā.

if(Flight::cache()->exists('simple-cache-test')) {
    // darīt kaut ko
}

Notīrīt kešatmiņu

Jūs izmantojat metodi flush(), lai notīrītu visu kešatmiņu.

Flight::cache()->flush();

Iegūt metadatus ar kešatmiņu

Ja vēlaties iegūt laika zīmogus un citus metadatus par kešatmiņas ierakstu, pārliecinieties, ka nododat true kā pareizo parametru.

$data = $cache->refreshIfExpired("simple-cache-meta-test", function () {
    echo "Refreshing data!" . PHP_EOL;
    return date("H:i:s"); // atgriezt datus, kas jākešo
}, 10, true); // true = atgriezt ar metadatiem
// vai
$data = $cache->get("simple-cache-meta-test", true); // true = atgriezt ar metadatiem

/*
Kehšatmiņas vienuma piemērs, kas iegūts ar metadatiem:
{
    "time":1511667506, <-- saglabāt unix laika zīmogu
    "expire":10,       <-- derīguma laiks sekundēs
    "data":"04:38:26", <-- deserializēti dati
    "permanent":false
}

Izmantojot metadatus, mēs varam, piemēram, aprēķināt, kad vienums tika saglabāts vai kad tas beidzas
Mēs varam arī piekļūt pašiem datiem, izmantojot atslēgu "data"
*/

$expiresin = ($data["time"] + $data["expire"]) - time(); // iegūt unix laika zīmogu, kad dati beidzas, un atņemt no tā pašreizējo laika zīmogu
$cacheddate = $data["data"]; // mēs piekļūstam pašiem datiem, izmantojot atslēgu "data"

echo "Latest cache save: $cacheddate, expires in $expiresin seconds";

Avota kods

Apmeklējiet https://github.com/flightphp/cache, lai skatītu kodu.

Awesome-plugins/permissions

FlightPHP/Permissions

Šis ir piekļuves tiesību modulis, kuru var izmantot savos projektos, ja jūsu lietotnē ir vairākas lomas un katrai lomai ir nedaudz atšķirīga funkcionalitāte. Šis modulis ļauj definēt piekļuves tiesības katrai lomai un pēc tam pārbaudīt, vai pašreizējam lietotājam ir tiesības piekļūt noteiktai lapai vai veikt noteiktu darbību.

Noklikšķiniet šeit, lai atvērtu repozitoriju GitHub.

Instalācija

Izpildiet composer require flightphp/permissions un esat gatavs!

Lietošana

Vispirms jāizveido piekļuves tiesības, pēc tam jāpastāsta lietotnei, ko tās nozīmē. Galu galā piekļuves tiesības pārbaudīsiet ar $Permissions->has(), ->can() vai is(). has() un can() ir vienāda funkcionalitāte, bet tiem ir atšķirīgi nosaukumi, lai jūsu kods būtu lasāmāks.

Pamata piemērs

Pieņemsim, ka jūsu lietotnē ir funkcija, kas pārbauda, vai lietotājs ir pieteicies. Jūs varat izveidot piekļuves tiesību objektu šādi:

// index.php
require 'vendor/autoload.php';

// kāds kods 

// tad jūs droši vien kaut ko izmantojat, kas jums pasaka, kāda ir pašreizējā lietotāja loma
// visticamāk, jums ir kaut kas, kas izvelk pašreizējo lomu
// no sesijas mainīgā, kas to definē
// pēc tam, kad kāds piesakās, pretējā gadījumā viņam būs 'guest' vai 'public' loma.
$current_role = 'admin';

// piekļuves tiesību iestatīšana
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
    return $current_role !== 'guest';
});

// Jūs droši vien vēlaties saglabāt šo objektu Flight kaut kur
Flight::set('permission', $permission);

Tad kādā kontrolierī varētu būt kaut kas līdzīgs šim.

<?php

// kāds kontrolieris
class SomeController {
    public function someAction() {
        $permission = Flight::get('permission');
        if ($permission->has('loggedIn')) {
            // darīt kaut ko
        } else {
            // darīt kaut ko citu
        }
    }
}

To varat izmantot arī, lai izsekotu, vai viņiem ir tiesības kaut ko darīt jūsu lietotnē. Piemēram, ja jums ir veids, kā lietotāji var mijiedarboties ar ziņām jūsu programmatūrā, varat pārbaudīt, vai viņiem ir tiesības veikt noteiktas darbības.

$current_role = 'admin';

// piekļuves tiesību iestatīšana
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
    if($current_role === 'admin') {
        $permissions = ['create', 'read', 'update', 'delete'];
    } else if($current_role === 'editor') {
        $permissions = ['create', 'read', 'update'];
    } else if($current_role === 'author') {
        $permissions = ['create', 'read'];
    } else if($current_role === 'contributor') {
        $permissions = ['create'];
    } else {
        $permissions = [];
    }
    return $permissions;
});
Flight::set('permission', $permission);

Tad kādā kontrolierī...

class PostController {
    public function create() {
        $permission = Flight::get('permission');
        if ($permission->can('post.create')) {
            // darīt kaut ko
        } else {
            // darīt kaut ko citu
        }
    }
}

Atkarību ievadīšana

Atkarības var ievadīt slēgtā funkcijā, kas definē piekļuves tiesības. Tas ir noderīgi, ja jums ir kāds slēdzis, id vai jebkurš cits datu punkts, pret kuru vēlaties pārbaudīt. Tas pats attiecas uz Class->Method tipa izsaukumiem, izņemot to, ka argumentus definējat metodē.

Slēgtās funkcijas

$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
    // ... kods
});

// jūsu kontrolierī
public function createOrder() {
    $MyDependency = Flight::myDependency();
    $permission = Flight::get('permission');
    if ($permission->can('order.create', $MyDependency)) {
        // darīt kaut ko
    } else {
        // darīt kaut ko citu
    }
}

Klases

namespace MyApp;

class Permissions {

    public function order(string $current_role, MyDependency $MyDependency = null) {
        // ... kods
    }
}

Īsceļš piekļuves tiesību iestatīšanai ar klasēm

Piekļuves tiesības var definēt arī ar klasēm. Tas ir noderīgi, ja jums ir daudz piekļuves tiesību un vēlaties saglabāt kodu tīru. Varat darīt kaut ko līdzīgu šim:

<?php

// inicializācijas kods
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');

// myapp/Permissions.php
namespace MyApp;

class Permissions {

    public function order(string $current_role, int $user_id) {
        // Pieņemot, ka esat to iestatījis iepriekš
        /** @var \flight\database\SimplePdo $db */
        $db = Flight::db();
        $allowed_permissions = [ 'read' ]; // visi var skatīt pasūtījumu
        if($current_role === 'manager') {
            $allowed_permissions[] = 'create'; // menedžeri var izveidot pasūtījumus
        }
        $some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
        if($some_special_toggle_from_db) {
            $allowed_permissions[] = 'update'; // ja lietotājam ir speciāls slēdzis, viņš var atjaunināt pasūtījumus
        }
        if($current_role === 'admin') {
            $allowed_permissions[] = 'delete'; // administratori var dzēst pasūtījumus
        }
        return $allowed_permissions;
    }
}

Interesantākais ir tas, ka ir arī īsceļš, ko var izmantot (ko var arī kešot!!!), kur vienkārši pasaka piekļuves tiesību klasei, lai tā kartētu visas metodes klasē uz piekļuves tiesībām. Tātad, ja jums ir metode ar nosaukumu order() un metode ar nosaukumu company(), tās automātiski tiks kartētas, lai jūs varētu vienkārši palaist $Permissions->has('order.read') vai $Permissions->has('company.read') un tas darbosies. To definēt ir ļoti grūti, tāpēc sekojiet man. Jums vienkārši jādara šādi:

Izveidojiet piekļuves tiesību klasi, ko vēlaties grupēt kopā.

class MyPermissions {
    public function order(string $current_role, int $order_id = 0): array {
        // kods piekļuves tiesību noteikšanai
        return $permissions_array;
    }

    public function company(string $current_role, int $company_id): array {
        // kods piekļuves tiesību noteikšanai
        return $permissions_array;
    }
}

Pēc tam padariet piekļuves tiesības atklājamas, izmantojot šo bibliotēku.

$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);

Visbeidzot, izsauciet piekļuves tiesības savā kodā, lai pārbaudītu, vai lietotājam ir atļauts veikt noteiktu darbību.

class SomeController {
    public function createOrder() {
        if(Flight::get('permissions')->can('order.create') === false) {
            die('You can\'t create an order. Sorry!');
        }
    }
}

Kešatmiņa

Lai iespējotu kešatmiņu, skatiet vienkāršo wruczak/phpfilecache bibliotēku. Zemāk ir piemērs tās iespējotai.


// šis $app var būt daļa no jūsu koda vai
// varat vienkārši nodot null un tas
// iegūs no Flight::app() konstruktorā
$app = Flight::app();

// Pašlaik tas pieņem failu kešatmiņu. Citi var viegli
// tikt pievienoti nākotnē. 
$Cache = new Wruczek\PhpFileCache\PhpFileCache;

$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // 3600 ir, cik sekundes to kešot. Atstājiet to tukšu, lai neizmantotu kešatmiņu

Un uz priekšu!

Awesome-plugins/simple_job_queue

Vienkārša Darba Rinda

Vienkārša Darba Rinda ir bibliotēka, ko var izmantot, lai apstrādātu darbus asinkroni. To var izmantot ar beanstalkd, MySQL/MariaDB, SQLite un PostgreSQL.

Instalēt

composer require n0nag0n/simple-job-queue

Izmantošana

Lai tas darbotos, jums nepieciešams veids, kā pievienot darbus rindai un veids, kā apstrādāt darbus (darbinieks). Tālāk ir sniegti piemēri, kā pievienot darbu rindai un kā apstrādāt darbu.

Pievienošana Flight

Šīs pievienošana Flight ir vienkārša un tiek veikta, izmantojot metodi register(). Tālāk ir piemērs, kā to pievienot Flight.

<?php
require 'vendor/autoload.php';

// Mainiet ['mysql'] uz ['beanstalkd'], ja vēlaties izmantot beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
    // ja jums jau ir PDO savienojums ar Flight::db();
    $Job_Queue->addQueueConnection(Flight::db());

    // vai, ja izmantojat beanstalkd/Pheanstalk
    $pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
    $Job_Queue->addQueueConnection($pheanstalk);
});

Jauna darba pievienošana

Kad pievienojat darbu, jums jānorāda caurule (rinda). Tas ir salīdzināms ar kanālu RabbitMQ vai cauruli beanstalkd.

<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));

Darbinieka palaišana

Šeit ir piemēra fails, kā palaist darbinieku.

<?php

require 'vendor/autoload.php';

$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO savienojums
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);

// vai, ja izmantojat beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);

$Job_Queue->watchPipeline('send_important_emails');
while(true) {
    $job = $Job_Queue->getNextJobAndReserve();

    // pielāgojiet, kā jums labāk guļ naktī (tikai datu bāzu rindām, beanstalkd šī instrukcija nav nepieciešama)
    if(empty($job)) {
        usleep(500000);
        continue;
    }

    echo "Apstrādā {$job['id']}\n";
    $payload = json_decode($job['payload'], true);

    try {
        $result = doSomethingThatDoesSomething($payload);

        if($result === true) {
            $Job_Queue->deleteJob($job);
        } else {
            // tas izņem to no gatavo rindu un ievieto citā rindā, kuru var paņemt un "izsist" vēlāk.
            $Job_Queue->buryJob($job);
        }
    } catch(Exception $e) {
        $Job_Queue->buryJob($job);
    }
}

Ilgu Procesu Apstrāde ar Supervisord

Supervisord ir procesu kontroles sistēma, kas nodrošina, ka jūsu darbinieku procesi paliek aktīvi nepārtraukti. Šeit ir detalizētāks ceļvedis, kā to iestatīt ar savu Vienkāršo Darba Rindu darbinieku:

Supervisord instalēšana

# Uz Ubuntu/Debian
sudo apt-get install supervisor

# Uz CentOS/RHEL
sudo yum install supervisor

# Uz macOS ar Homebrew
brew install supervisor

Darbinieka skripta izveide

Vispirms saglabājiet savu darbinieka kodu veltītā PHP failā:

<?php

require 'vendor/autoload.php';

$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO savienojums
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);

// Iestatiet cauruli, kuru vēlaties uzraudzīt
$Job_Queue->watchPipeline('send_important_emails');

// Ierakstiet darbinieka sākumu
echo date('Y-m-d H:i:s') . " - Darbinieks uzsākts\n";

while(true) {
    $job = $Job_Queue->getNextJobAndReserve();

    if(empty($job)) {
        usleep(500000); // Miega 0.5 sekundes
        continue;
    }

    echo date('Y-m-d H:i:s') . " - Apstrādā darba {$job['id']}\n";
    $payload = json_decode($job['payload'], true);

    try {
        $result = doSomethingThatDoesSomething($payload);

        if($result === true) {
            $Job_Queue->deleteJob($job);
            echo date('Y-m-d H:i:s') . " - Darbs {$job['id']} veiksmīgi pabeigts\n";
        } else {
            $Job_Queue->buryJob($job);
            echo date('Y-m-d H:i:s') . " - Darbs {$job['id']} neizdevās, aprakts\n";
        }
    } catch(Exception $e) {
        $Job_Queue->buryJob($job);
        echo date('Y-m-d H:i:s') . " - Izņēmums apstrādājot darbu {$job['id']}: {$e->getMessage()}\n";
    }
}

Supervisord konfigurēšana

Izveidojiet konfigurācijas failu savam darbiniekam:

[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d

Galvenās konfigurācijas opcijas:

Darbinieku pārvaldība ar Supervisorctl

Pēc konfigurācijas izveides vai modificēšanas:

# Pārlādēt supervisor konfigurāciju
sudo supervisorctl reread
sudo supervisorctl update

# Kontrolēt konkrētus darbinieku procesus
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*

Vairāku Cauruļu Palaišana

Vairāku cauruļu gadījumā izveidojiet atsevišķus darbinieku failus un konfigurācijas:

[program:email_worker]
command=php /path/to/email_worker.php
# ... citas konfigurācijas ...

[program:notification_worker]
command=php /path/to/notification_worker.php
# ... citas konfigurācijas ...

Uzraudzība un Ieraksti

Pārbaudiet ierakstus, lai uzraudzītu darbinieku aktivitāti:

# Apskatīt ierakstus
sudo tail -f /var/log/simple_job_queue.log

# Pārbaudīt statusu
sudo supervisorctl status

Šis iestatījums nodrošina, ka jūsu darba darbinieki turpina darboties, pat pēc avārijām, servera restartēšanas vai citiem jautājumiem, padarot jūsu rindu sistēmu uzticamu ražošanas vidēm.

Awesome-plugins/jwt

Firebase JWT - JSON Web Token autentifikācija

JWT (JSON Web Tokens) ir kompakts, URL-drošs veids, kā attēlot apgalvojumus starp jūsu lietojumprogrammu un klientu. Tie ir ideāli piemēroti bezstāvokļa API autentifikācijai — nav nepieciešama servera puses sesijas uzglabāšana! Šis ceļvedis parāda, kā integrēt Firebase JWT ar Flight drošai, tokenu balstītai autentifikācijai.

Apmeklējiet Github krātuvi, lai iegūtu pilnu dokumentāciju un detaļas.

Kas ir JWT?

JSON Web Token ir virkne, kas satur trīs daļas:

  1. Galvene: Metadati par tokenu (algoritms, tips)
  2. Ladējums: Jūsu dati (lietotāja ID, lomas, termiņš utt.)
  3. Paraksts: Kriptogrāfisks paraksts autentiskuma pārbaudei

Piemērs JWT: eyJ0eXAiOiJKV1QiLCJhbGc... (izskatās kā neskaidrs teksts, bet tas ir strukturēti dati!)

Kāpēc izmantot JWT?

Instalēšana

Instalējiet, izmantojot Composer:

composer require firebase/php-jwt

Pamata izmantošana

Šeit ir ātrs piemērs JWT izveidei un pārbaudei:

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// Jūsu slepenā atslēga (SAGLABĀJIE TO DROŠU!)
$secretKey = 'your-256-bit-secret-key-here-keep-it-safe';

// Izveidojiet tokenu
$payload = [
    'user_id' => 123,
    'username' => 'johndoe',
    'role' => 'admin',
    'iat' => time(),              // Izsniegts
    'exp' => time() + 3600        // Beigsies pēc 1 stundas
];

$jwt = JWT::encode($payload, $secretKey, 'HS256');
echo "Token: " . $jwt;

// Pārbaudiet un atšifrējiet tokenu
try {
    $decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));
    echo "User ID: " . $decoded->user_id;
} catch (Exception $e) {
    echo "Nederīgs tokens: " . $e->getMessage();
}

JWT starpprogrammatūra Flight (Ieteicamā pieeja)

Biežākais un noderīgākais veids, kā izmantot JWT ar Flight, ir kā starpprogrammatūru, lai aizsargātu jūsu API maršrutus. Šeit ir pilnīgs, ražošanai gatavs piemērs:

1. solis: Izveidojiet JWT starpprogrammatūras klasi

// app/middleware/JwtMiddleware.php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use flight\Engine;

class JwtMiddleware {

    protected Engine $app;
    protected string $secretKey;

    public function __construct(Engine $app) {
        $this->app = $app;
        // Saglabājiet savu slepeno atslēgu app/config/config.php, nevis cietkodus!
        $this->secretKey = $app->get('config')['jwt_secret'];
    }

    public function before(array $params) {
        $authHeader = $this->app->request()->getHeader('Authorization');

        // Pārbaudiet, vai pastāv Authorization galvene
        if (empty($authHeader)) {
            $this->app->jsonHalt(['error' => 'Nav nodrošināts autorizācijas tokens'], 401);
        }

        // Izvilciet tokenu no "Bearer <token>" formāta
        if (!preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
            $this->app->jsonHalt(['error' => 'Nederīgs autorizācijas formāts. Izmantojiet: Bearer <token>'], 401);
        }

        $jwt = $matches[1];

        try {
            // Atšifrējiet un pārbaudiet tokenu
            $decoded = JWT::decode($jwt, new Key($this->secretKey, 'HS256'));

            // Saglabājiet lietotāja datus pieprasījumā, lai izmantotu maršruta apstrādātājos
            $this->app->request()->data->user = $decoded;

        } catch (ExpiredException $e) {
            $this->app->jsonHalt(['error' => 'Tokens ir beidzies'], 401);
        } catch (SignatureInvalidException $e) {
            $this->app->jsonHalt(['error' => 'Nederīgs tokena paraksts'], 401);
        } catch (Exception $e) {
            $this->app->jsonHalt(['error' => 'Nederīgs tokens: ' . $e->getMessage()], 401);
        }
    }
}

2. solis: Reģistrējiet JWT slepeno atslēgu savā konfigurācijā

// app/config/config.php
return [
    'jwt_secret' => getenv('JWT_SECRET') ?: 'your-fallback-secret-for-development'
];

// app/config/bootstrap.php vai index.php
// pārliecinieties, ka pievienojat šo rindu, ja vēlaties pakļaut konfigurāciju lietotnei
$app->set('config', $config);

Drošības piezīme: Nekad necietkodējiet savu slepeno atslēgu! Izmantojiet vides mainīgos ražošanā.

3. solis: Aizsargājiet savus maršrutus ar starpprogrammatūru

// Aizsargājiet vienu maršrutu
Flight::route('GET /api/user/profile', function() {
    $user = Flight::request()->data->user; // Iestatīts ar starpprogrammatūru
    Flight::json([
        'user_id' => $user->user_id,
        'username' => $user->username,
        'role' => $user->role
    ]);
})->addMiddleware(JwtMiddleware::class);

// Aizsargājiet veselu maršrutu grupu (biežāk!)
Flight::group('/api', function() {
    Flight::route('GET /users', function() { /* ... */ });
    Flight::route('GET /posts', function() { /* ... */ });
    Flight::route('POST /posts', function() { /* ... */ });
    Flight::route('DELETE /posts/@id', function($id) { /* ... */ });
}, [ JwtMiddleware::class ]); // Visi maršruti šajā grupā ir aizsargāti!

Lai iegūtu vairāk detaļu par starpprogrammatūru, skatiet starpprogrammatūras dokumentāciju.

Biežas izmantošanas gadījumi

1. Pieteikšanās galapunkts (Tokenu ģenerēšana)

Izveidojiet maršrutu, kas ģenerē JWT pēc veiksmīgas autentifikācijas:

Flight::route('POST /api/login', function() {
    $data = Flight::request()->data;
    $username = $data->username ?? '';
    $password = $data->password ?? '';

    // Validējiet akreditācijas datus (piemērs — izmantojiet savu loģiku!)
    $user = validateUserCredentials($username, $password);

    if (!$user) {
        Flight::jsonHalt(['error' => 'Nederīgas akreditācijas'], 401);
    }

    // Ģenerējiet JWT
    $secretKey = Flight::get('config')['jwt_secret'];
    $payload = [
        'user_id' => $user->id,
        'username' => $user->username,
        'role' => $user->role,
        'iat' => time(),
        'exp' => time() + (60 * 60) // 1 stundas termiņš
    ];

    $jwt = JWT::encode($payload, $secretKey, 'HS256');

    Flight::json([
        'success' => true,
        'token' => $jwt,
        'expires_in' => 3600
    ]);
});

function validateUserCredentials($username, $password) {
    // Jūsu datubāzes meklēšana un paroles pārbaude šeit
    // Piemērs:
    $db = Flight::db();
    $user = $db->fetchRow("SELECT * FROM users WHERE username = ?", [$username]);

    if ($user && password_verify($password, $user['password_hash'])) {
        return (object) [
            'id' => $user['id'],
            'username' => $user['username'],
            'role' => $user['role']
        ];
    }
    return null;
}

2. Tokenu atjaunošanas plūsma

Implementējiet atjaunošanas tokenu sistēmu ilgstošām sesijām:

Flight::route('POST /api/login', function() {
    // ... validējiet akreditācijas datus ...

    $secretKey = Flight::get('config')['jwt_secret'];
    $refreshSecret = Flight::get('config')['jwt_refresh_secret'];

    // Īsa mūža piekļuves tokens (15 minūtes)
    $accessToken = JWT::encode([
        'user_id' => $user->id,
        'type' => 'access',
        'iat' => time(),
        'exp' => time() + (15 * 60)
    ], $secretKey, 'HS256');

    // Ilga mūža atjaunošanas tokens (7 dienas)
    $refreshToken = JWT::encode([
        'user_id' => $user->id,
        'type' => 'refresh',
        'iat' => time(),
        'exp' => time() + (7 * 24 * 60 * 60)
    ], $refreshSecret, 'HS256');

    Flight::json([
        'access_token' => $accessToken,
        'refresh_token' => $refreshToken,
        'expires_in' => 900
    ]);
});

Flight::route('POST /api/refresh', function() {
    $refreshToken = Flight::request()->data->refresh_token ?? '';
    $refreshSecret = Flight::get('config')['jwt_refresh_secret'];

    try {
        $decoded = JWT::decode($refreshToken, new Key($refreshSecret, 'HS256'));

        // Pārbaudiet, vai tas ir atjaunošanas tokens
        if ($decoded->type !== 'refresh') {
            Flight::jsonHalt(['error' => 'Nederīgs tokena tips'], 401);
        }

        // Ģenerējiet jaunu piekļuves tokenu
        $secretKey = Flight::get('config')['jwt_secret'];
        $accessToken = JWT::encode([
            'user_id' => $decoded->user_id,
            'type' => 'access',
            'iat' => time(),
            'exp' => time() + (15 * 60)
        ], $secretKey, 'HS256');

        Flight::json([
            'access_token' => $accessToken,
            'expires_in' => 900
        ]);

    } catch (Exception $e) {
        Flight::jsonHalt(['error' => 'Nederīgs atjaunošanas tokens'], 401);
    }
});

3. Lomu balstīta piekļuves kontrole

Paplašiniet savu starpprogrammatūru, lai pārbaudītu lietotāja lomas:

class JwtRoleMiddleware {

    protected Engine $app;
    protected array $allowedRoles;

    public function __construct(Engine $app, array $allowedRoles = []) {
        $this->app = $app;
        $this->allowedRoles = $allowedRoles;
    }

    public function before(array $params) {
        // Pieņemiet, ka JwtMiddleware jau darbojās un iestatīja lietotāja datus
        $user = $this->app->request()->data->user ?? null;

        if (!$user) {
            $this->app->jsonHalt(['error' => 'Nepieciešama autentifikācija'], 401);
        }

        // Pārbaudiet, vai lietotājam ir nepieciešamā loma
        if (!empty($this->allowedRoles) && !in_array($user->role, $this->allowedRoles)) {
            $this->app->jsonHalt(['error' => 'Nepietiekamas atļaujas'], 403);
        }
    }
}

// Lietojums: Administrātora tikai maršruts
Flight::route('DELETE /api/users/@id', function($id) {
    // Dzēst lietotāja loģika
})->addMiddleware([
    JwtMiddleware::class,
    new JwtRoleMiddleware(Flight::app(), ['admin'])
]);

4. Publiskais API ar ātruma ierobežošanu pa lietotāju

Izmantojiet JWT, lai izsekotu un ierobežotu ātrumu lietotājiem bez sesijām:

class RateLimitMiddleware {

    public function before(array $params) {
        $user = Flight::request()->data->user ?? null;
        $userId = $user ? $user->user_id : Flight::request()->ip;

        $cacheKey = "rate_limit:$userId";
        // Pārliecinieties, ka iestatāt kešošanas servisu app/config/services.php
        $requests = Flight::cache()->get($cacheKey, 0);

        if ($requests >= 100) { // 100 pieprasījumi stundā
            Flight::jsonHalt(['error' => 'Ātruma ierobežojums pārsniegts'], 429);
        }

        Flight::cache()->set($cacheKey, $requests + 1, 3600);
    }
}

Drošības labākās prakses

1. Izmantojiet stipras slepenās atslēgas

// Ģenerējiet drošu slepeno atslēgu (izpildiet vienreiz, saglabājiet .env failā)
$secretKey = base64_encode(random_bytes(32));
echo $secretKey; // Saglabājiet to savā .env failā!

2. Saglabājiet noslēpumus vides mainīgajos

// Nekad neiekļaujiet noslēpumus versiju kontrolē!
// Izmantojiet .env failu un bibliotēku, piemēram, vlucas/phpdotenv

// .env fails:
// JWT_SECRET=your-base64-encoded-secret-here
// JWT_REFRESH_SECRET=another-base64-encoded-secret-here

// Jūs varat arī izmantot app/config/config.php failu, lai saglabātu savus noslēpumus
// tikai pārliecinieties, ka konfigurācijas fails nav iekļauts versiju kontrolē
// return [
//     'jwt_secret' => 'your-base64-encoded-secret-here',
//     'jwt_refresh_secret' => 'another-base64-encoded-secret-here',
// ];

// Savā lietotnē:
$secretKey = getenv('JWT_SECRET');

3. Iestatiet piemērotus termiņus

// Labā prakse: īsa mūža piekļuves tokeni
'exp' => time() + (15 * 60)  // 15 minūtes

// Atjaunošanas tokeniem: ilgāks termiņš
'exp' => time() + (7 * 24 * 60 * 60)  // 7 dienas

4. Izmantojiet HTTPS ražošanā

JWT vienmēr jāpārsūta pār HTTPS. Nekad nesūtiet tokenus pār vienkāršu HTTP ražošanā!

5. Validējiet tokena apgalvojumus

Vienmēr validējiet apgalvojumus, kas jums rūp:

$decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));

// Pārbaude par termiņu tiek apstrādāta automātiski bibliotēkas
// Bet jūs varat pievienot pielāgotas validācijas:
if ($decoded->iat > time()) {
    throw new Exception('Tokens izmantots pirms izsniegšanas');
}

if (isset($decoded->nbf) && $decoded->nbf > time()) {
    throw new Exception('Tokens vēl nav derīgs');
}

6. Apsveriet tokenu melno sarakstu izrakstīšanai

Papildu drošībai uzturiet nederīgu tokenu melno sarakstu:

Flight::route('POST /api/logout', function() {
    $authHeader = Flight::request()->getHeader('Authorization');
    preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches);
    $jwt = $matches[1];

    // Izvilciet tokena termiņu
    $decoded = Flight::request()->data->user;
    $ttl = $decoded->exp - time();

    // Saglabājiet kešā/redis līdz termiņam
    Flight::cache()->set("blacklist:$jwt", true, $ttl);

    Flight::json(['message' => 'Veiksmīgi izrakstījies']);
});

// Pievienojiet savai JwtMiddleware:
public function before(array $params) {
    // ... izvilciet JWT ...

    // Pārbaudiet melno sarakstu
    if (Flight::cache()->get("blacklist:$jwt")) {
        $this->app->jsonHalt(['error' => 'Tokens ir atcelts'], 401);
    }

    // ... pārbaudiet tokenu ...
}

Algoritmi un atslēgu tipi

Firebase JWT atbalsta vairākus algoritmus:

Simetriskie algoritmi (HMAC)

$jwt = JWT::encode($payload, $secretKey, 'HS256');
$decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));

Asimetriskie algoritmi (RSA/ECDSA)

// Ģenerējiet atslēgas: openssl genrsa -out private.key 2048
// openssl rsa -in private.key -pubout -out public.key

$privateKey = file_get_contents('/path/to/private.key');
$publicKey = file_get_contents('/path/to/public.key');

// Kodējiet ar privāto atslēgu
$jwt = JWT::encode($payload, $privateKey, 'RS256');

// Atšifrējiet ar publisko atslēgu
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));

Kad izmantot RSA: Izmantojiet RSA, kad jums jāizplata publiskā atslēga pārbaudei (piem., mikroservisi, trešo pušu integrācijas). Vienai lietotnei HS256 ir vienkāršāks un pietiekams.

Traucējummeklēšana

"Beidzies tokens" kļūda

Jūsu tokena exp apgalvojums ir pagātnē. Izsniedziet jaunu tokenu vai implementējiet tokenu atjaunošanu.

"Paraksta pārbaude neizdevās"

use Firebase\JWT\JWT;

JWT::$leeway = 60; // Atļaujiet 60 sekunžu laika nobīdi
$decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));

Tokens netiek nosūtīts pieprasījumos

Pārliecinieties, ka jūsu klients nosūta Authorization galveni:

// JavaScript piemērs
fetch('/api/users', {
    headers: {
        'Authorization': 'Bearer ' + token
    }
});

Metodes

Firebase JWT bibliotēka nodrošina šīs pamatmetodes:

Kāpēc izmantot šo bibliotēku?

Skatīt arī

Licence

Firebase JWT bibliotēka ir licencēta saskaņā ar BSD 3-Klauzulas licenci. Skatiet Github krātuvi detaļām.

Awesome-plugins/n0nag0n_wordpress

WordPress Integrācija: n0nag0n/wordpress-integration-for-flight-framework

Vai vēlaties izmantot Flight PHP savā WordPress vietnē? Šis spraudnis to padara vienkāršu! Ar n0nag0n/wordpress-integration-for-flight-framework, jūs varat palaist pilnu Flight lietotni tieši blakus jūsu WordPress instalācijai—ideāli piemērots pielāgotu API, mikropakalpojumu vai pat pilnvērtīgu lietotņu izveidošanai, neizejot no WordPress komforta.


Ko tas dara?

Instalācija

  1. Augšupielādējiet flight-integration mapi uz jūsu /wp-content/plugins/ direktoriju.
  2. Aktivizējiet spraudni WordPress administrācijā (Plugins izvēlnē).
  3. Dodieties uz Settings > Flight Framework, lai konfigurētu spraudni.
  4. Iestatiet piegādātāja ceļu uz savu Flight instalāciju (vai izmantojiet Composer, lai instalētu Flight).
  5. Konfigurējiet savu lietotnes mapes ceļu un izveidojiet mapju struktūru (spraudnis var palīdzēt ar to!).
  6. Sāciet veidot savu Flight lietotni!

Izmantošanas piemēri

Pamata maršrutēšanas piemērs

Jūsu app/config/routes.php failā:

Flight::route('GET /api/hello', function() {
    Flight::json(['message' => 'Hello World!']);
});

Kontroliera piemērs

Izveidojiet kontrolieri app/controllers/ApiController.php:

namespace app\controllers;

use Flight;

class ApiController {
    public function getUsers() {
        // Jūs varat izmantot WordPress funkcijas iekš Flight!
        $users = get_users();
        $result = [];
        foreach($users as $user) {
            $result[] = [
                'id' => $user->ID,
                'name' => $user->display_name,
                'email' => $user->user_email
            ];
        }
        Flight::json($result);
    }
}

Pēc tam jūsu routes.php:

Flight::route('GET /api/users', [app\controllers\ApiController::class, 'getUsers']);

BUJ

J: Vai man jāzina Flight, lai izmantotu šo spraudni?
A: Jā, tas ir domāts izstrādātājiem, kuri vēlas izmantot Flight iekš WordPress. Ieteicams pamata zināšanas par Flight maršrutēšanu un pieprasījumu apstrādi.

J: Vai tas palēninās manu WordPress vietni?
A: Nē! Spraudnis apstrādā tikai pieprasījumus, kas atbilst jūsu Flight maršrutiem. Visi citi pieprasījumi tiek novirzīti uz WordPress kā parasti.

J: Vai es varu izmantot WordPress funkcijas savā Flight lietotnē?
A: Protams! Jums ir pilna piekļuve visām WordPress funkcijām, āķiem un globālajiem mainīgajiem no Flight maršrutiem un kontrolieriem.

J: Kā es varu izveidot pielāgotus maršrutus?
A: Definējiet savus maršrutus config/routes.php failā savā lietotnes mapē. Skatiet parauga failu, ko izveido mapju struktūras ģenerators, piemēriem.

Izmaiņu žurnāls

1.0.0
Sākotnējais izlaidums.


Papildus informācijai skatiet GitHub repo.

Awesome-plugins/ghost_session

Ghostff/Session

PHP Sesijas pārvaldnieks (nebloķējošs, zibspuldze, segments, sesijas šifrēšana). Izmanto PHP open_ssl, lai pēc izvēles šifrētu/dešifrētu sesijas datus. Atbalsta File, MySQL, Redis un Memcached.

Noklikšķiniet here, lai apskatītu kodu.

Instalācija

Instalējiet ar composer.

composer require ghostff/session

Pamata konfigurācija

Jums nav nepieciešams neko nodot, lai izmantotu noklusējuma iestatījumus ar savu sesiju. Vairāk par iestatījumiem varat lasīt Github Readme.

use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

// viena lieta, ko atcerēties, ir tā, ka jums ir jāapstiprina sava sesija katrā lapas ielādē
// vai jums būs nepieciešams izpildīt auto_commit savā konfigurācijā. 

Vienkāršs piemērs

Šeit ir vienkāršs piemērs, kā jūs varētu to izmantot.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // veiciet savu pieteikšanās loģiku šeit
    // validējiet paroli utt.

    // ja pieteikšanās ir veiksmīga
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // jebkurā laikā, kad jūs rakstāt uz sesiju, jums tas jāapstiprina apzināti.
    $session->commit();
});

// Šo pārbaudi varētu veikt ierobežotās lapas loģikā vai ietīt ar vidējo programmu.
Flight::route('/some-restricted-page', function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }

    // veiciet savu ierobežotās lapas loģiku šeit
});

// vidējās programmas versija
Flight::route('/some-restricted-page', function() {
    // regulāra lapas loģika
})->addMiddleware(function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }
});

Sarežģītāks piemērs

Šeit ir sarežģītāks piemērs, kā jūs varētu to izmantot.

use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// iestatiet pielāgotu ceļu uz savu sesijas konfigurācijas failu kā pirmo argumentu
// vai dodiet tam pielāgotu masīvu
$app->register('session', Session::class, [ 
    [
        // ja vēlaties glabāt savu sesijas datus datu bāzē (labi, ja vēlaties kaut ko tādu kā "izrakstīties no visām ierīcēm" funkcionalitāti)
        Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
        Session::CONFIG_ENCRYPT_DATA  => true,
        Session::CONFIG_SALT_KEY      => hash('sha256', 'my-super-S3CR3T-salt'), // lūdzu, mainiet to uz kaut ko citu
        Session::CONFIG_AUTO_COMMIT   => true, // dariet to tikai tad, ja tas ir nepieciešams un/vai grūti izpildīt commit() savu sesiju.
                                                // turklāt jūs varētu izdarīt Flight::after('start', function() { Flight::session()->commit(); });
        Session::CONFIG_MYSQL_DS         => [
            'driver'    => 'mysql',             # Datu bāzes draiveris PDO dns, piem. (mysql:host=...;dbname=...)
            'host'      => '127.0.0.1',         # Datu bāzes hosts
            'db_name'   => 'my_app_database',   # Datu bāzes nosaukums
            'db_table'  => 'sessions',          # Datu bāzes tabula
            'db_user'   => 'root',              # Datu bāzes lietotājvārds
            'db_pass'   => '',                  # Datu bāzes parole
            'persistent_conn'=> false,          # Izvairieties no jaunas savienojuma izveidošanas katru reizi, kad skriptam jārunā ar datu bāzi, kas padara tīmekļa lietojumprogrammu ātrāku. ATRASTIET SEVI PAŠI
        ]
    ] 
]);

Palīdzība! Mana sesijas dati netiek saglabāti!

Vai jūs iestatāt savus sesijas datus un tie netiek saglabāti starp pieprasījumiem? Jūs, iespējams, esat aizmirsis apstiprināt savus sesijas datus. Jūs varat to izdarīt, izsaucot $session->commit() pēc tam, kad esat iestatījis savus sesijas datus.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // veiciet savu pieteikšanās loģiku šeit
    // validējiet paroli utt.

    // ja pieteikšanās ir veiksmīga
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // jebkurā laikā, kad jūs rakstāt uz sesiju, jums tas jāapstiprina apzināti.
    $session->commit();
});

Cits veids, kā to apiet, ir tad, kad jūs iestatāt savu sesijas servisu, jums ir jāiestata auto_commit uz true savā konfigurācijā. Tas automātiski apstiprinās jūsu sesijas datus pēc katra pieprasījuma.

$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        $session->updateConfiguration([
            Session::CONFIG_AUTO_COMMIT   => true,
        ]);
    }
);

Turklāt jūs varētu izdarīt Flight::after('start', function() { Flight::session()->commit(); });, lai apstiprinātu savus sesijas datus pēc katra pieprasījuma.

Dokumentācija

Apmeklējiet Github Readme pilnai dokumentācijai. Konfigurācijas opcijas ir labi dokumentētas noklusējuma_config.php failā pašā. Kods ir vienkārši saprotams, ja jūs gribētu to izpētīt pats.

Awesome-plugins/mcp

FlightPHP MCP Server

FlightPHP MCP Server nodrošina jebkuram MCP savietojamam AI kodēšanas asistentam tūlītēju, strukturētu piekļuvi visai FlightPHP dokumentācijai — maršrutizācijai, starpprogrammatūrai, spraudņiem, ceļvežiem un citam. Tā vietā, lai jūsu AI izdomātu API detaļas vai minētos metožu parakstus, tas iegūst īsto dokumentāciju pēc pieprasījuma. Nav nepieciešamas API atslēgas, nav nepieciešama instalēšana viesotajai versijai.

Apmeklējiet Github repository, lai iegūtu pilnu avota kodu un detaļas.

Ātrais starts

Serveris ir publiski viesots un gatavs lietošanai:

https://mcp.flightphp.com/mcp

Vienkārši pievienojiet šo URL jūsu AI kodēšanas paplašinājumam. Nav reģistrācijas, nav akreditācijas datu. Skatiet sadaļu IDE Configuration zemāk, lai iegūtu kopēšanas-līmēšanas konfigurācijas populārākajām rīkiem.

Ko tas dara

Pēc savienojuma jūsu AI asistents var:

Galvenie punkti

IDE / AI Paplašinājuma konfigurācija

Serveris izmanto Streamable HTTP transportu. Izvēlieties savu paplašinājumu zemāk un ielīmējiet konfigurāciju.

Claude Code (CLI)

Palaidiet šādu komandu, lai pievienotu to savam projektam:

claude mcp add --transport http flightphp-docs https://mcp.flightphp.com/mcp

Vai pievienojiet manuāli sava projekta .mcp.json:

{
  "mcpServers": {
    "flightphp-docs": {
      "type": "http",
      "url": "https://mcp.flightphp.com/mcp"
    }
  }
}

GitHub Copilot (VS Code)

Pievienojiet .vscode/mcp.json savā darba telpā:

{
  "servers": {
    "flightphp-docs": {
      "type": "http",
      "url": "https://mcp.flightphp.com/mcp"
    }
  }
}

Kilo Code (VS Code)

Pievienojiet savam VS Code settings.json:

{
  "kilocode.mcpServers": {
    "flightphp-docs": {
      "url": "https://mcp.flightphp.com/mcp",
      "transport": "streamable-http"
    }
  }
}

Continue.dev (VS Code / JetBrains)

Pievienojiet ~/.continue/config.json:

{
  "mcpServers": [
    {
      "name": "flightphp-docs",
      "transport": {
        "type": "http",
        "url": "https://mcp.flightphp.com/mcp"
      }
    }
  ]
}

Pieejamie rīki

MCP serveris piedāvā šādus rīkus jūsu AI asistentam:

Rīks Apraksts
list_docs_pages Uzskaita visas pieejamās galvenās dokumentācijas tēmas ar slugiem un aprakstiem
get_docs_page Iegūst galvenās dokumentācijas lapu pēc tēmas slug (piem., routing, middleware, security)
list_guide_pages Uzskaita visus pieejamos soli pa solim ceļvežus
get_guide_page Iegūst pilnu ceļvedi pēc slug (piem., blog, unit-testing)
list_plugin_pages Uzskaita visas pieejamās spraudņu un paplašinājumu lapas
get_plugin_docs Iegūst pilnu spraudņu dokumentāciju pēc slug (piem., active-record, session, jwt)
search_docs Meklē visā dokumentācijā, ceļvežos un spraudņos pēc atslēgvārda vai tēmas
fetch_url Iegūst jebkuru lapu tieši pēc pilna docs.flightphp.com URL

Pašviesojums

Vēlaties palaidiet savu instances? Jums būs nepieciešams PHP >= 8.1 un Composer.

git clone https://github.com/flightphp/mcp.git
cd mcp
composer install
php server.php

Serveris palaižas uz http://0.0.0.0:8890/mcp pēc noklusējuma. Atjauniniet savu IDE konfigurāciju, lai norādītu uz jūsu lokālo adresi:

{
  "mcpServers": {
    "flightphp-docs": {
      "type": "http",
      "url": "http://localhost:8890/mcp"
    }
  }
}

Awesome-plugins/async

Async

Async ir mazs pakotnes Flight ietvaram, kas ļauj palaist jūsu Flight lietotnes asinhronos serveros un vidēs, piemēram, Swoole, AdapterMan, ReactPHP, Amp, RoadRunner, Workerman utt. No kastes tas ietver adapterus Swoole un AdapterMan.

Mērķis: izstrādāt un atkļūdot ar PHP-FPM (vai iebūvēto serveri) un pārslēgties uz Swoole (vai citu asinhrono draiveri) ražošanā ar minimālām izmaiņām.

Prasības

Instalēšana

Instalējiet caur composer:

composer require flightphp/async

Ja plānojat palaist ar Swoole, instalējiet paplašinājumu:

# izmantojot pecl
pecl install swoole
# vai openswoole
pecl install openswoole

# vai ar pakotņu pārvaldnieku (Debian/Ubuntu piemērs)
sudo apt-get install php-swoole

Ātrs Swoole piemērs

Zemāk ir minimāla iestatīšana, kas parāda, kā atbalstīt gan PHP-FPM (vai iebūvēto serveri), gan Swoole, izmantojot to pašu koda bāzi.

Faili, kas būs nepieciešami jūsu projektā:

index.php

Šis fails ir vienkāršs slēdzis, kas piespiež lietotni palaist PHP režīmā izstrādei.

// index.php
<?php

define('NOT_SWOOLE', true);

include 'swoole_server.php';

swoole_server.php

Šis fails inicializē jūsu Flight lietotni un sāks Swoole draiveri, kad NOT_SWOOLE nav definēts.

// swoole_server.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$app = Flight::app();

$app->route('/', function() use ($app) {
    $app->json(['hello' => 'world']);
});

if (!defined('NOT_SWOOLE')) {
    // Nepieciešams SwooleServerDriver klase, kad darbojas Swoole režīmā.
    require_once __DIR__ . '/SwooleServerDriver.php';

    Swoole\Runtime::enableCoroutine();
    $Swoole_Server = new SwooleServerDriver('127.0.0.1', 9501, $app);
    $Swoole_Server->start();
} else {
    $app->start();
}

SwooleServerDriver.php

Īss draiveris, kas parāda, kā savienot Swoole pieprasījumus ar Flight, izmantojot AsyncBridge un Swoole adapterus.

// SwooleServerDriver.php
<?php

use flight\adapter\SwooleAsyncRequest;
use flight\adapter\SwooleAsyncResponse;
use flight\AsyncBridge;
use flight\Engine;
use Swoole\HTTP\Server as SwooleServer;
use Swoole\HTTP\Request as SwooleRequest;
use Swoole\HTTP\Response as SwooleResponse;

class SwooleServerDriver {
    protected $Swoole;
    protected $app;

    public function __construct(string $host, int $port, Engine $app) {
        $this->Swoole = new SwooleServer($host, $port);
        $this->app = $app;

        $this->setDefault();
        $this->bindWorkerEvents();
        $this->bindHttpEvent();
    }

    protected function setDefault() {
        $this->Swoole->set([
            'daemonize'             => false,
            'dispatch_mode'         => 1,
            'max_request'           => 8000,
            'open_tcp_nodelay'      => true,
            'reload_async'          => true,
            'max_wait_time'         => 60,
            'enable_reuse_port'     => true,
            'enable_coroutine'      => true,
            'http_compression'      => false,
            'enable_static_handler' => true,
            'document_root'         => __DIR__,
            'static_handler_locations' => ['/css', '/js', '/images', '/.well-known'],
            'buffer_output_size'    => 4 * 1024 * 1024,
            'worker_num'            => 4,
        ]);

        $app = $this->app;
        $app->map('stop', function (?int $code = null) use ($app) {
            if ($code !== null) {
                $app->response()->status($code);
            }
        });
    }

    protected function bindHttpEvent() {
        $app = $this->app;
        $AsyncBridge = new AsyncBridge($app);

        $this->Swoole->on('Start', function(SwooleServer $server) {
            echo "Swoole http server is started at http://127.0.0.1:9501\n";
        });

        $this->Swoole->on('Request', function (SwooleRequest $request, SwooleResponse $response) use ($AsyncBridge) {
            $SwooleAsyncRequest = new SwooleAsyncRequest($request);
            $SwooleAsyncResponse = new SwooleAsyncResponse($response);

            $AsyncBridge->processRequest($SwooleAsyncRequest, $SwooleAsyncResponse);

            $response->end();
            gc_collect_cycles();
        });
    }

    protected function bindWorkerEvents() {
        $createPools = function() {
            // izveidojiet darbinieka specifiskas savienojumu kopas šeit
        };
        $closePools = function() {
            // aizveriet kopas / tīriet šeit
        };
        $this->Swoole->on('WorkerStart', $createPools);
        $this->Swoole->on('WorkerStop', $closePools);
        $this->Swoole->on('WorkerError', $closePools);
    }

    public function start() {
        $this->Swoole->start();
    }
}

Servera palaišana

Padoms: Ražošanā izmantojiet reverso proxy (Nginx) Swoole priekšā, lai apstrādātu TLS, statiskos failus un slodzes līdzsvarošanu.

Konfigurācijas piezīmes

Swoole draiveris piedāvā vairākas konfigurācijas opcijas:

Pielāgojiet šos, lai atbilstu jūsu resursiem un trafika modeļiem.

Kļūdu apstrāde

AsyncBridge pārvērš Flight kļūdas pareizās HTTP atbildēs. Jūs varat arī pievienot maršruta līmeņa kļūdu apstrādi:

$app->route('/*', function() use ($app) {
    try {
        // maršruta loģika
    } catch (Exception $e) {
        $app->response()->status(500);
        $app->json(['error' => $e->getMessage()]);
    }
});

AdapterMan un citas vidēs

AdapterMan tiek atbalstīts kā alternatīvs vidēs adapteris. Pakotne ir paredzēta pielāgošanai — pievienošana vai izmantošana citu adapteru parasti seko tam pašam modelim: pārveido servera pieprasījumu/atbildi par Flight pieprasījumu/atbildi caur AsyncBridge un vidēs specifiskajiem adapteriem.

Awesome-plugins/migrations

Migrācijas

Migrācija jūsu projektam uzrauga visus datu bāzes izmaiņas, kas saistītas ar jūsu projektu.
byjg/php-migration ir ļoti noderīga kodola bibliotēka, lai jūs varētu sākt.

Instalēšana

PHP bibliotēka

Ja vēlaties izmantot tikai PHP bibliotēku savā projektā:

composer require "byjg/migration"

Komandrindas interfeiss

Komandrindas interfeiss ir patstāvīgs un neprasa to instalēt kopā ar jūsu projektu.

Jūs varat instalēt globāli un izveidot simbolisko saiti.

composer require "byjg/migration-cli"

Lūdzu, apmeklējiet byjg/migration-cli, lai iegūtu vairāk informācijas par Migrācijas CLI.

Atbalstītās datu bāzes

Datu bāze Dzinējs Savienojuma virkne
Sqlite pdo_sqlite sqlite:///path/to/file
MySql/MariaDb pdo_mysql mysql://lietotāja_vārds:parole@hostname:ports/datu_bāze
Postgres pdo_pgsql pgsql://lietotāja_vārds:parole@hostname:ports/datu_bāze
Sql Server pdo_dblib, pdo_sysbase Linux dblib://lietotāja_vārds:parole@hostname:ports/datu_bāze
Sql Server pdo_sqlsrv Windows sqlsrv://lietotāja_vārds:parole@hostname:ports/datu_bāze

Kā tas darbojas?

Datu bāzes migrācija izmanto TĪRU SQL, lai pārvaldītu datu bāzes versijas.
Lai tas darbotos, jums nepieciešams:

SQL skripti

Skripti ir sadalīti trīs skriptu grupās:

Skriptu direktorija ir:

 <root dir>
     |
     +-- base.sql
     |
     +-- /migrations
              |
              +-- /up
                   |
                   +-- 00001.sql
                   +-- 00002.sql
              +-- /down
                   |
                   +-- 00000.sql
                   +-- 00001.sql

Multi izstrādes vide

Ja strādājat ar vairākiem izstrādātājiem un vairākiem zara, ir grūti noteikt nākamo numuru.

Šajā gadījumā jums ir papildinājums "-dev" pēc versijas numura.

Skatiet scenāriju:

Abos gadījumos izstrādātāji izveidos failu ar nosaukumu 43-dev.sql. Abi izstrādātāji pārskatīs UP un DOWN bez problēmām, un jūsu lokālā versija būs 43.

Bet izstrādātājs 1 apvieno jūsu izmaiņas un izveido gala versiju 43.sql (git mv 43-dev.sql 43.sql). Ja izstrādātājs 2 atjauninās jūsu vietējo zaru, viņam būs fails 43.sql (no izstrādātāja 1) un jūsu fails 43-dev.sql.
Ja viņš mēģina migrēt UP vai DOWN, migrācijas skripts uzrakstīs un brīdinās viņu, ka ir DIVAS versijas 43. Šajā gadījumā izstrādātājam 2 būs jāatjaunina jūsu fails uz 44-dev.sql un jāturpina strādāt līdz apvienojat jūsu izmaiņas un ģenerējat gala versiju.

PHP API izmantošana un integrēšana savos projektos

Pamatlietošana ir

Skatiet piemēru:

<?php
// Izveidojiet savienojuma URI
// Skatiet vairāk: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');

// Reģistrējiet datu bāzi vai datu bāzes, kas var apstrādāt šo URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// Izveidojiet migrācijas instance
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// Pievienojiet atgriezenisko saiti progresam, lai saņemtu informāciju par izpildi
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
    echo "$action, $currentVersion, ${fileInfo['description']}\n";
});

// Atjaunojiet datu bāzi, izmantojot "base.sql" skriptu
// un palaidiet VISUS esošos skriptus, lai paaugstinātu datu bāzes versiju līdz jaunākajai versijai
$migration->reset();

// Palaidiet VISUS esošos skriptus, lai paaugstinātu vai samazinātu datu bāzes versiju
// no pašreizējās versijas līdz $version numuram;
// Ja versijas numurs nav noteikts, migrējiet līdz pēdējai datu bāzes versijai
$migration->update($version = null);

Migrācijas objekts kontrolē datu bāzes versiju.

Izstrādājot versiju kontroli savā projektā

<?php
// Reģistrējiet datu bāzi vai datu bāzes, kas var apstrādāt šo URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// Izveidojiet migrācijas instance
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// Šī komanda izveidos versijas tabulu jūsu datu bāzē
$migration->createVersion();

Iegūstot pašreizējo versiju

<?php
$migration->getCurrentVersion();

Pievienojiet atgriezenisko saiti, lai kontrolētu progresu

<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
    echo "Veicot komandu: $command pie versijas $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});

Iegūstot Db Dzinēja instanci

<?php
$migration->getDbDriver();

Lai to izmantotu, lūdzu, apmeklējiet: https://github.com/byjg/anydataset-db

Daļēju migrāciju novēršana (nav pieejama MySQL)

Daļēja migrācija ir tad, ja migrācijas skripts tiek pārtraukts procesa vidū kļūdas vai manuālas pārtraukšanas dēļ.

Migrācijas tabula būs ar statusu daļējs uz augšu vai daļējs uz leju, un to nepieciešams labot manuāli, pirms var atkārtoti migrēt.

Lai izvairītos no šīs situācijas, jūs varat norādīt, ka migrācija tiks izpildīta transakcijas kontekstā.
Ja migrācijas skripts neizdodas, transakcija tiks atcelta, un migrācijas tabula tiks iezīmēta kā pabeigta, un versija būs tūlītēji iepriekšējā versija pirms skripta, kas izraisīja kļūdu.

Lai šo funkciju aktivizētu, jums jāizsauc metode withTransactionEnabled, nododot true kā parametru:

<?php
$migration->withTransactionEnabled(true);

PIEZĪME: Šī funkcija nav pieejama MySQL, jo tas neatbalsta DDL komandas transakcijas ietvaros.
Ja jūs izmantosiet šo metodi ar MySQL, migrācija to klusi ignorēs.
Vairāk informācijas: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html

Ieteikumi SQL migrāciju rakstīšanai Postgres

Par trigeru un SQL funkciju izveidi

-- DARI
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- Pārbaudiet, vai ir norādīts empname un alga
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname nedrīkst būt null'; -- nav svarīgi, vai šie komentāri ir tukši vai nē
        END IF; --

        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% nedrīkst būt null alga', NEW.empname; --
        END IF; --

        -- Kas strādā pie mums, kad viņiem par to jāmaksā?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% nedrīkst būt negatīva alga', NEW.empname; --
        END IF; --

        -- Atcerieties, kurš izmainīja algu, kad
        NEW.last_date := current_timestamp; --
        NEW.last_user := current_user; --
        RETURN NEW; --
    END; --
$emp_stamp$ LANGUAGE plpgsql;


-- NEDARI
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- Pārbaudiet, vai ir norādīts empname un alga
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname nedrīkst būt null';
        END IF;
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% nedrīkst būt null alga', NEW.empname;
        END IF;

        -- Kas strādā pie mums, kad viņiem par to jāmaksā?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% nedrīkst būt negatīva alga', NEW.empname;
        END IF;

        -- Atcerieties, kurš izmainīja algu, kad
        NEW.last_date := current_timestamp;
        NEW.last_user := current_user;
        RETURN NEW;
    END;
$emp_stamp$ LANGUAGE plpgsql;

Tā kā PDO datu bāzes abstrakcijas slānis nevar izpildīt SQL komandu partijas, kad byjg/migration lasa migrācijas failu, tas ir jāizdala visus SQL faila saturus pie semikolu un jāizpilda komandas viena pa viena.
Tomēr ir viens veids, kā komanda var saturēt vairākas semikolas tās ķermenī: funkcijas.

Lai pareizi analizētu funkcijas, byjg/migration 2.1.0 sāka sadalīt migrācijas failus pēc semikola + EOL secības, nevis tikai pēc semikolas.
Šādā veidā, ja jūs pievienojat tukšu komentāru pēc katra iekšējā semikola funkcijas definīcijā, byjg/migration to varēs pareizi analizēt.

Diemžēl, ja jūs aizmirsīsiet pievienot kādu no šiem komentāriem, bibliotēka sadalīs CREATE FUNCTION paziņojumu multiple parts, un migrācija neizdosies.

Izvairieties no kolonnas rakstzīmes (:)

-- DARI
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
  check_in   DATE NOT NULL
);


-- NEDARI
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
  check_in   DATE NOT NULL
);

Tā kā PDO izmanto kolonnas rakstzīmi, lai prefixētu nosauktos parametrus sagatavotās komandas, tās izmantošana izraisa to, ka tas aptrūkst citos kontekstos.

Piemēram, PostgreSQL komandas var izmantot ::, lai konvertētu vērtības starp tipiem.
No otras puses, PDO to uztvers kā nederīgu nosauktu parametru nederīgā kontekstā un neizdosies, kad tas mēģinās to izpildīt.

Vienīgā veida, kā izlabot šo neatbilstību, ir pilnībā izvairīties no kolonnām (šajā gadījumā PostgreSQL ir alternatīva sintakse: CAST(value AS type)).

Izmantojiet SQL redaktoru

Visbeidzot, manuālas SQL migrāciju rakstīšana var būt nogurdinoša, taču to ir ievērojami vieglāk izdarīt, ja izmantojat redaktoru, kas spēj saprast SQL sintaksi, piedāvā pabeigšanu, introspektē jūsu pašreizējo datu bāzes shēmu un / vai automātiski formatē jūsu kodu.

Dažādu migrāciju apstrāde vienā shēmā

Ja jums ir jāizveido dažādi migrācijas skripti un versijas vienā shēmā, tas ir iespējams, bet tas ir ļoti riskanti un es neieteiktu to darīt.

Lai to izdarītu, jums jāizveido dažādas "migrācijas tabulas", nododot parametru konstruktora parametrā.

<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "JAUNA_MIGRĀCIJAS_TABULAS_NOSAUKUMS");

Drošības apsvērumu dēļ šī funkcija nav pieejama komandrindā, bet jūs varat izmantot vides mainīgo MIGRATION_VERSION, lai glabātu nosaukumu.

Mēs patiešām iesakām neizmantot šo funkciju. Ieteikums ir viena migrācija vienai shēmā.

Vienības testu izpilde

Pamatvienības testus var izpildīt ar:

vendor/bin/phpunit

Datu bāzu testu izpilde

Lai veiktu integrācijas testus, jums jābūt datu bāzēm, kas darbojas. Mēs esam nodrošinājuši pamata docker-compose.yml, un jūs varat to izmantot, lai uzsāktu datu bāzes testēšanai.

Datu bāzu palaidīšana

docker-compose up -d postgres mysql mssql

Testu izpilde

vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*

Pēc izvēles jūs varat iestatīt resursdatora un paroles iestatījumus, ko izmanto vienības testos.

export MYSQL_TEST_HOST=localhost     # noklusējums uz localhost
export MYSQL_PASSWORD=newpassword    # izmantojiet '.', ja vēlaties nulles paroli
export PSQL_TEST_HOST=localhost      # noklusējums uz localhost
export PSQL_PASSWORD=newpassword     # izmantojiet '.', ja vēlaties nulles paroli
export MSSQL_TEST_HOST=localhost     # noklusējums uz localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db      # noklusējums uz /tmp/test.db

Awesome-plugins/comment_template

CommentTemplate

CommentTemplate ir jaudīgs PHP veidņu dzinējs ar resursu kompilāciju, veidņu mantojumu un mainīgo apstrādi. Tas nodrošina vienkāršu, bet elastīgu veidu, kā pārvaldīt veidnes ar iebūvētu CSS/JS minimizāciju un kešošanu.

Funkcijas

Instalēšana

Instalējiet ar composer.

composer require knifelemon/comment-template

Pamata konfigurācija

Ir dažas pamata konfigurācijas opcijas, lai sāktu. Vairāk par tām var lasīt CommentTemplate Repo.

1. metode: Izmantojot atgriezeniskās saites funkciju

<?php
require_once 'vendor/autoload.php';

use KnifeLemon\CommentTemplate\Engine;

$app = Flight::app();

$app->register('view', Engine::class, [], function (Engine $engine) use ($app) {
    // Saknes direktorija (kur ir index.php) - jūsu tīmekļa lietojumprogrammas dokumentu sakne
    $engine->setPublicPath(__DIR__);

    // Veidņu failu direktorija - atbalsta gan relatīvās, gan absolūtās ceļus
    $engine->setSkinPath('views');             // Relatīvi pret publisko ceļu

    // Kur tiks glabāti kompilētie resursi - atbalsta gan relatīvās, gan absolūtās ceļus
    $engine->setAssetPath('assets');           // Relatīvi pret publisko ceļu

    // Veidnes faila paplašinājums
    $engine->setFileExtension('.php');
});

$app->map('render', function(string $template, array $data) use ($app): void {
    echo $app->view()->render($template, $data);
});

2. metode: Izmantojot konstruktoru parametrus

<?php
require_once 'vendor/autoload.php';

use KnifeLemon\CommentTemplate\Engine;

$app = Flight::app();

// __construct(string $publicPath = "", string $skinPath = "", string $assetPath = "", string $fileExtension = "")
$app->register('view', Engine::class, [
    __DIR__,                // publicPath - saknes direktorija (kur ir index.php)
    'views',                // skinPath - veidņu ceļš (atbalsta relatīvos/absolūtos)
    'assets',               // assetPath - kompilēto resursu ceļš (atbalsta relatīvos/absolūtos)
    '.php'                  // fileExtension - veidnes faila paplašinājums
]);

$app->map('render', function(string $template, array $data) use ($app): void {
    echo $app->view()->render($template, $data);
});

Ceļu konfigurācija

CommentTemplate nodrošina inteliģentu ceļu apstrādi gan relatīvajiem, gan absolūtajiem ceļiem:

Publiskais ceļš

Publiskais ceļš ir jūsu tīmekļa lietojumprogrammas saknes direktorija, parasti tur, kur atrodas index.php. Tas ir dokumentu saknes ceļš, no kura tīmekļa serveri pasniedz failus.

// Piemērs: ja jūsu index.php ir /var/www/html/myapp/index.php
$template->setPublicPath('/var/www/html/myapp');  // Saknes direktorija

// Windows piemērs: ja jūsu index.php ir C:\xampp\htdocs\myapp\index.php
$template->setPublicPath('C:\\xampp\\htdocs\\myapp');

Veidņu ceļa konfigurācija

Veidņu ceļš atbalsta gan relatīvos, gan absolūtos ceļus:

$template = new Engine();
$template->setPublicPath('/var/www/html/myapp');  // Saknes direktorija (kur ir index.php)

// Relatīvie ceļi - automātiski apvienoti ar publisko ceļu
$template->setSkinPath('views');           // → /var/www/html/myapp/views/
$template->setSkinPath('templates/pages'); // → /var/www/html/myapp/templates/pages/

// Absolūtie ceļi - izmantoti kā ir (Unix/Linux)
$template->setSkinPath('/var/www/templates');      // → /var/www/templates/
$template->setSkinPath('/full/path/to/templates'); // → /full/path/to/templates/

// Windows absolūtie ceļi
$template->setSkinPath('C:\\www\\templates');     // → C:\www\templates\
$template->setSkinPath('D:/projects/templates');  // → D:/projects/templates/

// UNC ceļi (Windows tīkla koplietošana)
$template->setSkinPath('\\\\server\\share\\templates'); // → \\server\share\templates\

Resursu ceļa konfigurācija

Resursu ceļš arī atbalsta gan relatīvos, gan absolūtos ceļus:

// Relatīvie ceļi - automātiski apvienoti ar publisko ceļu
$template->setAssetPath('assets');        // → /var/www/html/myapp/assets/
$template->setAssetPath('static/files');  // → /var/www/html/myapp/static/files/

// Absolūtie ceļi - izmantoti kā ir (Unix/Linux)
$template->setAssetPath('/var/www/cdn');           // → /var/www/cdn/
$template->setAssetPath('/full/path/to/assets');   // → /full/path/to/assets/

// Windows absolūtie ceļi
$template->setAssetPath('C:\\www\\static');       // → C:\www\static\
$template->setAssetPath('D:/projects/assets');    // → D:/projects/assets/

// UNC ceļi (Windows tīkla koplietošana)
$template->setAssetPath('\\\\server\\share\\assets'); // → \\server\share\assets\

Inteliģenta ceļu noteikšana:

Kā tas darbojas:

Tracy atkļūdotāja integrācija

CommentTemplate ietver integrāciju ar Tracy Debugger izstrādes reģistrēšanai un atkļūdošanai.

Comment Template Tracy

Instalācija

composer require tracy/tracy

Lietošana

<?php
use KnifeLemon\CommentTemplate\Engine;
use Tracy\Debugger;

// Iespējot Tracy (jāizsauc pirms jebkuras izvades)
Debugger::enable(Debugger::DEVELOPMENT);
Flight::set('flight.content_length', false);

// Veidnes pārrakstīšana
$app->register('view', Engine::class, [], function (Engine $builder) use ($app) {
    $builder->setPublicPath($app->get('flight.views.topPath'));
    $builder->setAssetPath($app->get('flight.views.assetPath'));
    $builder->setSkinPath($app->get('flight.views.path'));
    $builder->setFileExtension($app->get('flight.views.extension'));
});
$app->map('render', function(string $template, array $data) use ($app): void {
    echo $app->view()->render($template, $data);
});

$app->start();

Atkļūdošanas paneļa funkcijas

CommentTemplate pievieno pielāgotu paneli Tracy atkļūdošanas joslai ar četrām cilnēm:

Kas tiek reģistrēts

Piezīme: Nulles ietekme uz veiktspēju, ja Tracy nav instalēts vai ir atspējots.

Skatiet pilnīgu darba piemēru ar Flight PHP.

Veidnes direktīvas

Izkārtojuma mantojums

Izmantojiet izkārtojumus, lai izveidotu kopīgu struktūru:

layout/global_layout.php:

<!DOCTYPE html>
<html>
<head>
    <title>{$title}</title>
</head>
<body>
    <!--@contents-->
</body>
</html>

view/page.php:

<!--@layout(layout/global_layout)-->
<h1>{$title}</h1>
<p>{$content}</p>

Resursu pārvaldība

CSS faili

<!--@css(/css/styles.css)-->          <!-- Minimizēts un kešots -->
<!--@cssSingle(/css/critical.css)-->  <!-- Atsevišķs fails, ne minimizēts -->

JavaScript faili

CommentTemplate atbalsta dažādas JavaScript ielādes stratēģijas:

<!--@js(/js/script.js)-->             <!-- Minimizēts, ielādēts apakšā -->
<!--@jsAsync(/js/analytics.js)-->     <!-- Minimizēts, ielādēts apakšā ar async -->
<!--@jsDefer(/js/utils.js)-->         <!-- Minimizēts, ielādēts apakšā ar defer -->
<!--@jsTop(/js/critical.js)-->        <!-- Minimizēts, ielādēts galvā -->
<!--@jsTopAsync(/js/tracking.js)-->   <!-- Minimizēts, ielādēts galvā ar async -->
<!--@jsTopDefer(/js/polyfill.js)-->   <!-- Minimizēts, ielādēts galvā ar defer -->
<!--@jsSingle(/js/widget.js)-->       <!-- Atsevišķs fails, ne minimizēts -->
<!--@jsSingleAsync(/js/ads.js)-->     <!-- Atsevišķs fails, ne minimizēts, async -->
<!--@jsSingleDefer(/js/social.js)-->  <!-- Atsevišķs fails, ne minimizēts, defer -->

Resursu direktīvas CSS/JS failos

CommentTemplate arī apstrādā resursu direktīvas CSS un JavaScript failos kompilācijas laikā:

CSS piemērs:

/* Jūsu CSS failos */
/* Fontu definīcijas */
@font-face {
    font-family: 'CustomFont';
    src: url('<!--@asset(fonts/custom.woff2)-->') format('woff2');
}

.background-image {
    background: url('<!--@asset(images/bg.jpg)-->');
}

.inline-icon {
    background: url('<!--@base64(icons/star.svg)-->');
}

JavaScript piemērs:

/* Jūsu JS failos */
const fontUrl = '<!--@asset(fonts/custom.woff2)-->';
const imageData = '<!--@base64(images/icon.png)-->';

Base64 kodēšana

<!--@base64(images/logo.png)-->       <!-- Iekšēji kā datu URI -->

Piemērs:

<!-- Iekšēji mazas bildes kā datu URI ātrākai ielādei -->
<img src="<!--@base64(images/logo.png)-->" alt="Logo">
<div style="background-image: url('<!--@base64(icons/star.svg)-->');">
    Mazs ikona kā fons
</div>

Resursu kopēšana

<!--@asset(images/photo.jpg)-->       <!-- Kopē atsevišķu resursu uz publisko direktoriju -->
<!--@assetDir(assets)-->              <!-- Kopē visu direktoriju uz publisko direktoriju -->

Piemērs:

<!-- Kopē un atsaucas uz statiskajiem resursiem -->
<img src="<!--@asset(images/hero-banner.jpg)-->" alt="Hero Banner">
<a href="<!--@asset(documents/brochure.pdf)-->" download>Lejupielādēt Brošūru</a>

<!-- Kopē visu direktoriju (fonti, ikonas utt.) -->
<!--@assetDir(assets/fonts)-->
<!--@assetDir(assets/icons)-->

Veidnes iekļaušana

<!--@import(components/header)-->     <!-- Iekļauj citas veidnes -->

Piemērs:

<!-- Iekļauj atkārtoti izmantojamas sastāvdaļas -->
<!--@import(components/header)-->

<main>
    <h1>Sveiki mūsu vietnē</h1>
    <!--@import(components/sidebar)-->

    <div class="content">
        <p>Galvenais saturs šeit...</p>
    </div>
</main>

<!--@import(components/footer)-->

Mainīgo apstrāde

Pamata mainīgie

<h1>{$title}</h1>
<p>{$description}</p>

Mainīgo filtri

{$title|upper}                       <!-- Pārvērš uz lielajiem burtiem -->
{$content|lower}                     <!-- Pārvērš uz mazajiem burtiem -->
{$html|striptag}                     <!-- Noņem HTML atzīmes -->
{$text|escape}                       <!-- Ekrēno HTML -->
{$multiline|nl2br}                   <!-- Pārvērš jaunas rindas uz <br> -->
{$html|br2nl}                        <!-- Pārvērš <br> atzīmes uz jaunām rindām -->
{$description|trim}                  <!-- Apgriež tukšumus -->
{$subject|title}                     <!-- Pārvērš uz virsraksta gadījumu -->

Mainīgo komandas

{$title|default=Default Title}       <!-- Iestata noklusējuma vērtību -->
{$name|concat= (Admin)}              <!-- Apvieno tekstu -->

Mainīgo komandas

{$content|striptag|trim|escape}      <!-- Ķēžu vairākus filtrus -->

Komentāri

Veidnes komentāri tiek pilnībā noņemti no izvades un neparādīsies galīgajā HTML:

{* Tas ir vienrindas veidnes komentārs *}

{* 
   Tas ir vairākrindu 
   veidnes komentārs 
   kas aptver vairākas rindas
*}

<h1>{$title}</h1>
{* Debug komentārs: pārbauda, vai title mainīgais darbojas *}
<p>{$content}</p>

Piezīme: Veidnes komentāri {* ... *} atšķiras no HTML komentāriem <!-- ... -->. Veidnes komentāri tiek noņemti apstrādes laikā un nekad nenonāk pārlūkprogrammā.

Piemēra projekta struktūra

project/
├── source/
│   ├── layouts/
│   │   └── default.php
│   ├── components/
│   │   ├── header.php
│   │   └── footer.php
│   ├── css/
│   │   ├── bootstrap.min.css
│   │   └── custom.css
│   ├── js/
│   │   ├── app.js
│   │   └── bootstrap.min.js
│   └── homepage.php
├── public/
│   └── assets/           # Ģenerētie resursi
│       ├── css/
│       └── js/
└── vendor/

Awesome-plugins/easy_query

EasyQuery

knifelemon/easy-query ir viegls, plūstošs SQL vaicājumu veidotājs, kas ģenerē SQL un parametrus sagatavotiem vaicājumiem. Darbojas ar SimplePdo.

Funkcijas

Instalēšana

composer require knifelemon/easy-query

Ātrais Sākums

use KnifeLemon\EasyQuery\Builder;

$q = Builder::table('users')
    ->select(['id', 'name', 'email'])
    ->where(['status' => 'active'])
    ->orderBy('created_at DESC')
    ->limit(10)
    ->build();

// Izmantojiet ar Flight's SimplePdo
$users = Flight::db()->fetchAll($q['sql'], $q['params']);

Saprašana build()

build() metode atgriež masīvu ar sql un params. Šī atdalīšana uztur jūsu datu bāzi drošu, izmantojot sagatavotus vaicājumus.

$q = Builder::table('users')
    ->where(['email' => 'user@example.com'])
    ->build();

// Atgriež:
// [
//     'sql' => 'SELECT * FROM users WHERE email = ?',
//     'params' => ['user@example.com']
// ]

Vaicājumu Veidi

SELECT

// Izvēlieties visas kolonnas
$q = Builder::table('users')->build();
// SELECT * FROM users

// Izvēlieties specifiskas kolonnas
$q = Builder::table('users')
    ->select(['id', 'name', 'email'])
    ->build();
// SELECT id, name, email FROM users

// Ar tabulas segvārdu
$q = Builder::table('users')
    ->alias('u')
    ->select(['u.id', 'u.name'])
    ->build();
// SELECT u.id, u.name FROM users AS u

INSERT

$q = Builder::table('users')
    ->insert([
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'status' => 'active'
    ])
    ->build();
// INSERT INTO users SET name = ?, email = ?, status = ?

Flight::db()->runQuery($q['sql'], $q['params']);
$userId = Flight::db()->lastInsertId();

UPDATE

$q = Builder::table('users')
    ->update(['status' => 'inactive', 'updated_at' => date('Y-m-d H:i:s')])
    ->where(['id' => 123])
    ->build();
// UPDATE users SET status = ?, updated_at = ? WHERE id = ?

Flight::db()->runQuery($q['sql'], $q['params']);

DELETE

$q = Builder::table('users')
    ->delete()
    ->where(['id' => 123])
    ->build();
// DELETE FROM users WHERE id = ?

Flight::db()->runQuery($q['sql'], $q['params']);

COUNT

$q = Builder::table('users')
    ->count()
    ->where(['status' => 'active'])
    ->build();
// SELECT COUNT(*) AS cnt FROM users WHERE status = ?

$count = Flight::db()->fetchField($q['sql'], $q['params']);

WHERE Nosacījumi

Vienkārša Vienlīdzība

$q = Builder::table('users')
    ->where(['id' => 123, 'status' => 'active'])
    ->build();
// WHERE id = ? AND status = ?

Salīdzinājuma Operatori

$q = Builder::table('users')
    ->where([
        'age' => ['>=', 18],
        'score' => ['<', 100],
        'name' => ['!=', 'admin']
    ])
    ->build();
// WHERE age >= ? AND score < ? AND name != ?

LIKE

$q = Builder::table('users')
    ->where(['name' => ['LIKE', '%john%']])
    ->build();
// WHERE name LIKE ?

IN / NOT IN

// IN
$q = Builder::table('users')
    ->where(['id' => ['IN', [1, 2, 3, 4, 5]]])
    ->build();
// WHERE id IN (?, ?, ?, ?, ?)

// NOT IN
$q = Builder::table('users')
    ->where(['status' => ['NOT IN', ['banned', 'deleted']]])
    ->build();
// WHERE status NOT IN (?, ?)

BETWEEN

$q = Builder::table('products')
    ->where(['price' => ['BETWEEN', [100, 500]]])
    ->build();
// WHERE price BETWEEN ? AND ?

OR Nosacījumi

Izmantojiet orWhere(), lai pievienotu OR grupētus nosacījumus:

$q = Builder::table('users')
    ->where(['status' => 'active'])
    ->orWhere([
        'role' => 'admin',
        'permissions' => ['LIKE', '%manage%']
    ])
    ->build();
// WHERE status = ? AND (role = ? OR permissions LIKE ?)

JOIN

Iekšējais JOIN

$q = Builder::table('users')
    ->alias('u')
    ->select(['u.id', 'u.name', 'p.title'])
    ->innerJoin('posts', 'u.id = p.user_id', 'p')
    ->build();
// SELECT u.id, u.name, p.title FROM users AS u INNER JOIN posts AS p ON u.id = p.user_id

Kreisais JOIN

$q = Builder::table('users')
    ->alias('u')
    ->select(['u.name', 'o.total'])
    ->leftJoin('orders', 'u.id = o.user_id', 'o')
    ->build();
// ... LEFT JOIN orders AS o ON u.id = o.user_id

Vairāki JOIN

$q = Builder::table('orders')
    ->alias('o')
    ->select(['o.id', 'u.name AS customer', 'p.title AS product'])
    ->innerJoin('users', 'o.user_id = u.id', 'u')
    ->leftJoin('order_items', 'o.id = oi.order_id', 'oi')
    ->leftJoin('products', 'oi.product_id = p.id', 'p')
    ->where(['o.status' => 'completed'])
    ->build();

Sakārtošana, Grupēšana un Ierobežojumi

ORDER BY

$q = Builder::table('users')
    ->orderBy('created_at DESC')
    ->build();
// ORDER BY created_at DESC

GROUP BY

$q = Builder::table('orders')
    ->select(['user_id', 'COUNT(*) as order_count'])
    ->groupBy('user_id')
    ->build();
// SELECT user_id, COUNT(*) as order_count FROM orders GROUP BY user_id

LIMIT un OFFSET

$q = Builder::table('users')
    ->limit(10)
    ->build();
// LIMIT 10

$q = Builder::table('users')
    ->limit(10, 20)  // limit, offset
    ->build();
// LIMIT 10 OFFSET 20

Neapstrādātas SQL Izteiksmes

Izmantojiet raw(), kad nepieciešamas SQL funkcijas vai izteiksmes, kas nedrīkst tikt apstrādātas kā saistīti parametri.

Pamata Neapstrādāts

$q = Builder::table('users')
    ->update([
        'login_count' => Builder::raw('login_count + 1'),
        'updated_at' => Builder::raw('NOW()')
    ])
    ->where(['id' => 123])
    ->build();
// SET login_count = login_count + 1, updated_at = NOW()

Neapstrādāts ar Saistītiem Parametriem

$q = Builder::table('orders')
    ->update([
        'total' => Builder::raw('COALESCE(subtotal, ?) + ?', [0, 10])
    ])
    ->where(['id' => 1])
    ->build();
// SET total = COALESCE(subtotal, ?) + ?
// params: [0, 10, 1]

Neapstrādāts WHERE (Apakšvaicājums)

$q = Builder::table('products')
    ->where([
        'price' => ['>', Builder::raw('(SELECT AVG(price) FROM products)')]
    ])
    ->build();
// WHERE price > (SELECT AVG(price) FROM products)

Droši Identifikatori Lietotāja Ievadei

Kad kolonnu nosaukumi nāk no lietotāja ievades, izmantojiet safeIdentifier(), lai novērstu SQL injekcijas:

$sortColumn = $_GET['sort'];  // piem., 'created_at'
$safeColumn = Builder::safeIdentifier($sortColumn);

$q = Builder::table('users')
    ->orderBy($safeColumn . ' DESC')
    ->build();

// Ja lietotājs mēģina: "name; DROP TABLE users--"
// Izmet InvalidArgumentException

rawSafe Lietotāja Norādītiem Kolonnu Nosaukumiem

$userColumn = $_GET['aggregate_column'];

$q = Builder::table('orders')
    ->select([
        Builder::rawSafe('SUM({col})', ['col' => $userColumn])->value . ' AS total'
    ])
    ->build();
// Validē kolonnu nosaukumu, izmet izņēmumu, ja nederīgs

Brīdinājums: Nekad neiekļaujiet lietotāja ievadi tieši raw(). Vienmēr izmantojiet saistītus parametrus vai safeIdentifier().


Vaicājuma Veidotāja atkārtota Izmantošana

Notīrīšanas Metodes

Notīriet specifiskas daļas, lai atkārtoti izmantotu veidotāju:

$query = Builder::table('users')
    ->select(['id', 'name'])
    ->where(['status' => 'active'])
    ->orderBy('created_at DESC');

// Pirmais vaicājums
$q1 = $query->limit(10)->build();

// Notīriet un atkārtoti izmantojiet
$query->clearWhere()->clearLimit();

// Otrais vaicājums ar atšķirīgiem nosacījumiem
$q2 = $query
    ->where(['status' => 'pending'])
    ->limit(5)
    ->build();

Pieejamās Notīrīšanas Metodes

Metode Apraksts
clearWhere() Notīra WHERE nosacījumus un parametrus
clearSelect() Atstata SELECT kolonnas uz noklusējuma '*'
clearJoin() Notīra visus JOIN pantus
clearGroupBy() Notīra GROUP BY pantu
clearOrderBy() Notīra ORDER BY pantu
clearLimit() Notīra LIMIT un OFFSET
clearAll() Atstata veidotāju uz sākotnējo stāvokli

Piemērs ar Lapināšanu

$baseQuery = Builder::table('users')
    ->select(['id', 'name', 'email'])
    ->where(['status' => 'active'])
    ->orderBy('created_at DESC');

// Iegūstiet kopējo skaitu
$countQuery = clone $baseQuery;
$countResult = $countQuery->clearSelect()->count()->build();
$total = Flight::db()->fetchField($countResult['sql'], $countResult['params']);

// Iegūstiet lapinātos rezultātus
$page = 1;
$perPage = 20;
$listResult = $baseQuery->limit($perPage, ($page - 1) * $perPage)->build();
$users = Flight::db()->fetchAll($listResult['sql'], $listResult['params']);

Dinamiska Vaicājuma Veidošana

$query = Builder::table('products')->alias('p');

if (!empty($categoryId)) {
    $query->where(['p.category_id' => $categoryId]);
}

if (!empty($minPrice)) {
    $query->where(['p.price' => ['>=', $minPrice]]);
}

if (!empty($maxPrice)) {
    $query->where(['p.price' => ['<=', $maxPrice]]);
}

if (!empty($searchTerm)) {
    $query->where(['p.name' => ['LIKE', "%{$searchTerm}%"]]);
}

$result = $query->orderBy('p.created_at DESC')->limit(20)->build();
$products = Flight::db()->fetchAll($result['sql'], $result['params']);

Pilns FlightPHP Piemērs

use KnifeLemon\EasyQuery\Builder;

// Lietotāju saraksts ar lapināšanu
Flight::route('GET /users', function() {
    $page = (int) (Flight::request()->query['page'] ?? 1);
    $perPage = 20;

    $q = Builder::table('users')
        ->select(['id', 'name', 'email', 'created_at'])
        ->where(['status' => 'active'])
        ->orderBy('created_at DESC')
        ->limit($perPage, ($page - 1) * $perPage)
        ->build();

    $users = Flight::db()->fetchAll($q['sql'], $q['params']);
    Flight::json(['users' => $users, 'page' => $page]);
});

// Lietotāja izveide
Flight::route('POST /users', function() {
    $data = Flight::request()->data;

    $q = Builder::table('users')
        ->insert([
            'name' => $data->name,
            'email' => $data->email,
            'created_at' => Builder::raw('NOW()')
        ])
        ->build();

    Flight::db()->runQuery($q['sql'], $q['params']);
    Flight::json(['id' => Flight::db()->lastInsertId()]);
});

// Lietotāja atjaunināšana
Flight::route('PUT /users/@id', function($id) {
    $data = Flight::request()->data;

    $q = Builder::table('users')
        ->update([
            'name' => $data->name,
            'email' => $data->email,
            'updated_at' => Builder::raw('NOW()')
        ])
        ->where(['id' => $id])
        ->build();

    Flight::db()->runQuery($q['sql'], $q['params']);
    Flight::json(['success' => true]);
});

// Lietotāja dzēšana
Flight::route('DELETE /users/@id', function($id) {
    $q = Builder::table('users')
        ->delete()
        ->where(['id' => $id])
        ->build();

    Flight::db()->runQuery($q['sql'], $q['params']);
    Flight::json(['success' => true]);
});

API Atsauce

Statiskās Metodes

Metode Apraksts
Builder::table(string $table) Izveido jaunu veidotāja instanci tabulai
Builder::raw(string $sql, array $bindings = []) Izveido neapstrādātu SQL izteiksmi
Builder::rawSafe(string $expr, array $identifiers, array $bindings = []) Neapstrādāta izteiksme ar drošu identifikatora aizstāšanu
Builder::safeIdentifier(string $identifier) Validē un atgriež drošu kolonnas/tabula nosaukumu

Instanču Metodes

Metode Apraksts
alias(string $alias) Iestata tabulas segvārdu
select(string\|array $columns) Iestata kolonnas izvēlei (noklusējums: '*')
where(array $conditions) Pievieno WHERE nosacījumus (AND)
orWhere(array $conditions) Pievieno OR WHERE nosacījumus
join(string $table, string $condition, string $alias, string $type) Pievieno JOIN pantu
innerJoin(string $table, string $condition, string $alias) Pievieno Iekšējo JOIN
leftJoin(string $table, string $condition, string $alias) Pievieno Kreisais JOIN
groupBy(string $groupBy) Pievieno GROUP BY pantu
orderBy(string $orderBy) Pievieno ORDER BY pantu
limit(int $limit, int $offset = 0) Pievieno LIMIT un OFFSET
count(string $column = '*') Iestata vaicājumu uz COUNT
insert(array $data) Iestata vaicājumu uz INSERT
update(array $data) Iestata vaicājumu uz UPDATE
delete() Iestata vaicājumu uz DELETE
build() Veido un atgriež ['sql' => ..., 'params' => ...]
get() Aliasam build()

Tracy Debugger Integrācija

EasyQuery automātiski integrējas ar Tracy Debugger, ja instalēts. Nav nepieciešama iestatīšana!

composer require tracy/tracy
use Tracy\Debugger;

Debugger::enable();

// Visi vaicājumi automātiski tiek reģistrēti Tracy panelī
$q = Builder::table('users')->where(['status' => 'active'])->build();

Tracy panelis rāda:

Pilnai dokumentācijai apmeklējiet GitHub repozitoriju.

Awesome-plugins/twig

Twig

Twig ir elastīgs, ātrs un drošs PHP veidņu dzinējs. Tā ir veidņu valoda, ko izmanto Symfony un daudzi citi projekti, kas nozīmē, ka mākslīgā intelekta kodēšanas rīki un lielākā daļa PHP izstrādātāju jau labi zina tās sintaksi. Twig kompilē veidnes uz optimizētu PHP, automātiski aizsargā izvadi pēc noklusējuma (lieliski XSS aizsardzībai) un ir viegli paplašināms ar filtriem, funkcijām un paplašinājumiem.

Instalācija

Instalējiet ar composer.

composer require twig/twig

Pamata konfigurācija

Ir dažas pamata konfigurācijas opcijas, lai sāktu darbu. Vairāk par tām var lasīt Twig dokumentācijā.

require 'vendor/autoload.php';

$app = Flight::app();

$app->map('render', function(string $template, array $data): void {
    $loader = new \Twig\Loader\FilesystemLoader(Flight::get('flight.views.path'));
    $twig = new \Twig\Environment($loader, [
        // Kur Twig glabā kompilētās veidnes
        'cache' => __DIR__ . '/../cache/twig',
        // Pārkompilēt veidnes, kad mainās avots (ērti izstrādes laikā)
        'auto_reload' => true,
    ]);

    echo $twig->render($template, $data);
});

Twig reģistrēšana kā skata klasi

Ja vēlaties atkārtoti izmantot vienu Twig vidi (ieteicams ražošanai), reģistrējiet to un norādiet render uz to:

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('view', \Twig\Environment::class, [
    new \Twig\Loader\FilesystemLoader($app->get('flight.views.path')),
    [
        'cache' => __DIR__ . '/../cache/twig',
        'auto_reload' => true,
    ],
]);

$app->map('render', function(string $template, array $data): void {
    echo Flight::view()->render($template, $data);
});

Vienkāršs izkārtojuma piemērs

Šeit ir vienkāršs izkārtojuma faila piemērs. Šis ir fails, kas tiks izmantots, lai ietītu visas jūsu citas skatnes.

{# app/views/layout.twig #}
<!doctype html>
<html lang="en">
    <head>
        <title>{% if title %}{{ title }} - {% endif %}My App</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <nav>
                {# jūsu navigācijas elementi šeit #}
            </nav>
        </header>
        <div id="content">
            {# Šeit ir burvība #}
            {% block content %}{% endblock %}
        </div>
        <div id="footer">
            &copy; Copyright
        </div>
    </body>
</html>

Un tagad mums ir jūsu fails, kas tiks renderēts šajā satura blokā:

{# app/views/home.twig #}
{# Tas saka Twig, ka šis fails ir "iekšpusē" layout.twig failā #}
{% extends 'layout.twig' %}

{# Tas ir saturs, kas tiks renderēts izkārtojumā satura blokā #}
{% block content %}
    <h1>Home Page</h1>
    <p>Welcome to my app!</p>
{% endblock %}

Tad, kad renderējat to savā funkcijā vai kontrolierī, jūs darītu kaut ko līdzīgu šim:

// vienkāršs maršruts
Flight::route('/', function () {
    Flight::render('home.twig', [
        'title' => 'Home Page'
    ]);
});

// vai ja izmantojat kontrolieri
Flight::route('/', [HomeController::class, 'index']);

// HomeController.php
class HomeController
{
    public function index()
    {
        Flight::render('home.twig', [
            'title' => 'Home Page'
        ]);
    }
}

Skatiet Twig dokumentāciju lai iegūtu vairāk informācijas par to, kā izmantot Twig pilnā potenciālā!

Atkļūdošana

Twig nāk ar Atkļūdošanas paplašinājumu, kas pievieno dump() funkciju, ko varat izmantot veidnēs. Iespējojiet to tikai izstrādes laikā:

$app->register('view', \Twig\Environment::class, [
    new \Twig\Loader\FilesystemLoader($app->get('flight.views.path')),
    [
        'cache' => __DIR__ . '/../cache/twig',
        'debug' => true, // nepieciešams dump() funkcijai
        'auto_reload' => true,
    ],
], function (\Twig\Environment $twig): void {
    $twig->addExtension(new \Twig\Extension\DebugExtension());
});

Tad veidnē:

{{ dump(user) }}

Jūs varat arī savienot Twig ar Tracy PHP līmeņa atkļūdošanai. Veidņu līmeņa metrikām (renderēšanas laiks, atmiņa, kuras veidnes/bloki darbināti), izmantojiet izvēles Twig paneli flightphp/tracy-extensions: nododiet Twig\Profiler\Profiletwig_profile uz TracyExtensionLoader. Izvēles TwigTracyExtension atklāj {{ dump() }} / {{ bdump() }} / {{ dumpe() }} veidnēs, kad Tracy ir ieslēgts.

Drošības piezīme

Twig automātiski aizsargā izvadi pēc noklusējuma, kas palīdz aizsargāt pret XSS uzbrukumiem. Dodiet priekšroku {{ variable }} tekstam. Izmantojiet tikai |raw filtru, kad jūs apzināti uzticaties HTML saturam (piemēram, sanitizēts markdown, ko jūs jau apstrādājāt servera pusē).

Awesome-plugins/session

FlightPHP Sesija - Viegls failu balstīts sesijas pārvaldnieks

Tas ir viegls, failu balstīts sesijas pārvaldnieka spraudnis Flight PHP Framework. Tas nodrošina vienkāršu, bet spēcīgu risinājumu sesiju pārvaldībai, ar iespējām kā nesekojošas sesijas lasīšanas, izvēles šifrēšanu, automātisku apstiprināšanu un testēšanas režīmu attīstībai. Sesijas dati tiek glabāti failos, padarot to ideālu lietojumprogrammām, kurām nav nepieciešama datubāze.

Ja vēlaties izmantot datubāzi, pārbaudiet ghostff/session spraudni, kas satur daudzas no šīm pašām iespējām, bet ar datubāzes aizmuguri.

Apmeklējiet Github repozitoriju pilnam avota kodam un detaļām.

Instalēšana

Instalējiet spraudni caur Composer:

composer require flightphp/session

Pamata lietošana

Lūk, vienkāršs piemērs, kā izmantot flightphp/session spraudni jūsu Flight lietojumprogrammā:

require 'vendor/autoload.php';

use flight\Session;

$app = Flight::app();

// Reģistrē sesijas servisu
$app->register('session', Session::class);

// Piemērs maršrutam ar sesijas lietošanu
Flight::route('/login', function() {
    $session = Flight::session();
    $session->set('user_id', 123);
    $session->set('username', 'johndoe');
    $session->set('is_admin', false);

    echo $session->get('username'); // Izvada: johndoe
    echo $session->get('preferences', 'default_theme'); // Izvada: default_theme

    if ($session->get('user_id')) {
        Flight::json(['message' => 'Lietotājs ir pieteicies!', 'user_id' => $session->get('user_id')]);
    }
});

Flight::route('/logout', function() {
    $session = Flight::session();
    $session->clear(); // Notīra visas sesijas datus
    Flight::json(['message' => 'Izlogojies veiksmīgi']);
});

Flight::start();

Galvenie punkti

Konfigurācija

Jūs varat pielāgot sesijas pārvaldnieku, nododot masīvu ar opcijām reģistrējot:

// Jā, tas ir dubults masīvs :)
$app->register('session', Session::class, [ [
    'save_path' => '/custom/path/to/sessions',         // Direktorija sesijas failiem
    'prefix' => 'myapp_',                              // Priekša sesijas failiem
    'encryption_key' => 'a-secure-32-byte-key-here',   // Ieslēgt šifrēšanu (32 baiti ieteikti AES-256-CBC)
    'auto_commit' => false,                            // Atslēgt automātisko apstiprināšanu manuālai kontrolei
    'start_session' => true,                           // Sākt sesiju automātiski (pēc noklusējuma: true)
    'test_mode' => false,                              // Ieslēgt testēšanas režīmu attīstībai
    'serialization' => 'json',                         // Serializācijas metode: 'json' (pēc noklusējuma) vai 'php' (legacy)
] ]);

Konfigurācijas opcijas

Opcija Apraksts Noklusētā vērtība
save_path Direktorija, kur glabājas sesijas faili sys_get_temp_dir() . '/flight_sessions'
prefix Priekša saglabātajam sesijas failam sess_
encryption_key Atslēga AES-256-CBC šifrēšanai (izvēles) null (bez šifrēšanas)
auto_commit Automātiski saglabāt sesijas datus izslēgšanās brīdī true
start_session Sākt sesiju automātiski true
test_mode Darboties testēšanas režīmā bez ietekmēšanas uz PHP sesijām false
test_session_id Pielāgota sesijas ID testēšanas režīmam (izvēles) Nejauši ģenerēta, ja nav iestatīta
serialization Serializācijas metode: 'json' (pēc noklusējuma, droša) vai 'php' (legacy, atļauj objektus) 'json'

Serializācijas režīmi

Pēc noklusējuma šī bibliotēka izmanto JSON serializāciju sesijas datiem, kas ir droša un novērš PHP objektu iesūkšanās ievainojamības. Ja jums ir nepieciešams glabāt PHP objektus sesijā (nav ieteicams lielākumam app), jūs varat izvēlēties legacy PHP serializāciju:

Piezīme: Ja izmantojat JSON serializāciju, mēģinot glabāt objektu, tiks izraisīta izņēmuma kļūda.

Paplašināta lietošana

Manuāla apstiprināšana

Ja atslēdzat automātisko apstiprināšanu, jums ir manuāli jāapstiprina izmaiņas:

$app->register('session', Session::class, ['auto_commit' => false]);

Flight::route('/update', function() {
    $session = Flight::session();
    $session->set('key', 'value');
    $session->commit(); // Skaidri saglabā izmaiņas
});

Sesijas drošība ar šifrēšanu

Ieslēdziet šifrēšanu sensitīviem datiem:

$app->register('session', Session::class, [
    'encryption_key' => 'your-32-byte-secret-key-here'
]);

Flight::route('/secure', function() {
    $session = Flight::session();
    $session->set('credit_card', '4111-1111-1111-1111'); // Šifrēts automātiski
    echo $session->get('credit_card'); // Dekodēts pie saņemšanas
});

Sesijas atjaunošana

Atjaunojiet sesijas ID drošībai (piemēram, pēc pieteikšanās):

Flight::route('/post-login', function() {
    $session = Flight::session();
    $session->regenerate(); // Jauns ID, saglabā datus
    // VAI
    $session->regenerate(true); // Jauns ID, dzēš vecos datus
});

Middleware piemērs

Aizsargājiet maršrutus ar sesijas balstītu autentifikāciju:

Flight::route('/admin', function() {
    Flight::json(['message' => 'Laipni lūgts admin panelī']);
})->addMiddleware(function() {
    $session = Flight::session();
    if (!$session->get('is_admin')) {
        Flight::halt(403, 'Piekļuve liegta');
    }
});

Tas ir tikai vienkāršs piemērs, kā to izmantot middleware. Vairāk detalizētam piemēram, skatiet middleware dokumentāciju.

Metodes

Session klase nodrošina šādas metodes:

Visas metodes izņemot get() un id() atgriež Session instanci ķēdes izveidošanai.

Kāpēc izmantot šo spraudni?

Tehniskās detaļas

Dalība

Ieteikumi ir laipni gaidīti! Forkojiet repozitoriju, veiciet izmaiņas un iesniedziet pull request. Ziņojiet par kļūdām vai ieteikumiām caur Github issue tracker.

Licence

Šis spraudnis ir licencēts zem MIT Licence. Skatiet Github repozitoriju detaļām.

Awesome-plugins/runway

Runway

Runway ir CLI lietotne, kas palīdz pārvaldīt jūsu Flight lietotnes. Tā var ģenerēt kontrolierus, parādīt visas maršrutus, palaist AI iestatīšanas palīgus, migrācijas (skeletonā) un vēl vairāk. Tā ir balstīta uz izcilo adhocore/php-cli bibliotēku.

Klikšķiniet šeit, lai skatītu kodu.

Scaffolding komandas ir apzināti saskaņotas ar oficiālo skeleton, lai AI kodēšanas rīki un cilvēki katru reizi iegūtu vienādus ceļus, vārdu telpas un konstruktora injekcijas stilu.

Instalācija

Instalējiet ar composer.

composer require flightphp/runway

Skeleton jau atkarīgs no Runway; izmantojiet php runway no projekta saknes.

Pamata konfigurācija

Pirmo reizi palaižot Runway, tas mēģinās atrast runway konfigurāciju app/config/config.php caur 'runway' atslēgu.

<?php
// app/config/config.php
return [
    'runway' => [
        'app_root' => 'app/',
        'public_root' => 'public/',
        // neobligāti; skeleton arī izmanto index_root publiskajai ieejai
        'index_root' => 'public/index.php',
    ],
];

PIEZĪME - Sākot ar v1.2.0, .runway-config.json ir novecojis par labu app/config/config.php. Migrējiet ar php runway config:migrate, kad atjaunināt vecākus projektus. Skeleton joprojām var izveidot nelielu .runway-config.json create-project laikā saderībai; turpmāk dodiet priekšroku runway atslēgai config.php.

Projekta saknes noteikšana

Runway ir pietiekami gudrs, lai noteiktu jūsu projekta sakni, pat ja to palaižat no apakšdirektorijas. Tas meklē indikatorus, piemēram, composer.json, .git vai app/config/config.php, lai noteiktu, kur atrodas projekta sakne. Tas nozīmē, ka Runway komandas varat palaist no jebkuras vietas savā projektā!

Lietošana

Runway ir vairākas komandas, kuras varat izmantot Flight lietotnes pārvaldīšanai. Ir divi vienkārši veidi, kā izmantot Runway.

  1. Ja izmantojat skeleton projektu, varat palaist php runway [komanda] no sava projekta saknes.
  2. Ja izmantojat Runway kā composer instalētu pakotni, varat palaist vendor/bin/runway [komanda] no sava projekta saknes.

Komandu saraksts

Visu pieejamo komandu sarakstu varat apskatīt, palaižot php runway komandu.

php runway

Paļaujieties tikai uz komandām, kas faktiski parādās šajā sarakstā jūsu instalācijā (pamata Runway komandas pret projektu specifiskām, piemēram, skeleton migrate).

Komandas palīdzība

Jebkurai komandai varat nodot --help karodziņu, lai iegūtu vairāk informācijas par to, kā izmantot komandu.

php runway routes --help
php runway make:controller --help

Šeit ir daži piemēri:

Kontroliera ģenerēšana

make:controller veido kontroliera skeletu, kas atbilst oficiālajam skeleton izkārtojumam:

Ceļš app/Controller/{Nosaukums}.php
Vārdu telpa App\Controller
Stils flight\Engine konstruktora injekcija (bez Flight:: klases ķermenī)
php runway make:controller MyController
# → app/Controller/MyController.php
#   namespace App\Controller;

Paredzamās formas piemērs (vienkāršots):

<?php

declare(strict_types=1);

namespace App\Controller;

use flight\Engine;

class MyController
{
    protected Engine $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function index(): void
    {
        // piem. $this->app->render('…', […]);
    }
}

Reģistrējiet to ar klases izsaucamajam, lai Dice varētu izveidot kontrolieri:

// app/config/routes.php
use App\Controller\MyController;

$router->get('/mine', [MyController::class, 'index']);

Kāpēc šis izkārtojums? Mapes reģistram jāatbilst vārdu telpai (Controller nevis controllers) Composer PSR-4 Linux sistēmām — skatiet Autoloadēšanu. Tas pats ceļš ir tas, ko root un scoped AGENTS.md faili saka AI rīkiem izmantot, lai ģenerētie un rokām rakstītie kontrolieri paliktu identiski.

Vecākā dokumentācijā un kopienas projektos dažreiz tika izmantots app/controllers/ un app\controllers. Tas joprojām ir derīgs, ja jūsu kokā joprojām tiek izmantotas mazie burti mapēm. Jauni skeleton projekti un pašreizējā make:controller izvade izmanto app/Controller/ + App\Controller.

Active Record modeļa ģenerēšana

Vispirms pārliecinieties, ka esat instalējis Active Record spraudni.

php runway make:record users

Oficiālajā skeleton modeļi atrodas app/Model/ ar vārdu telpu App\Model, un DB savienojums ir SimplePdo (injicējiet to vai nododiet ActiveRecord konstruktoram). Ģenerētie failu nosaukumi/vārdu telpas seko Runway pašreizējiem noklusējumiem un jūsu runway konfigurācijai — dodiet priekšroku jaunu modeļu saskaņošanai ar App\Model, lai tie atbilstu autoloadēšanai un AGENTS.md.

Oficiālā skeleton posts demonstrācijas modelim atbilstošs piemērs:

<?php

declare(strict_types=1);

namespace App\Model;

use flight\ActiveRecord;

/**
 * @property int $id
 * @property string $title
 * // …
 */
class Post extends ActiveRecord
{
    protected array $relations = [];

    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'posts');
    }
}

Ja vecāks ģenerators joprojām izvada app/records / app\records, varat saglabāt šo konvenciju mantojuma lietotnēs vai pārvietot failus uz app/Model/ un atjaunināt vārdu telpu, lai atbilstu mapes reģistram.

Migrācijas (skeleton)

Oficiālais skeleton nodrošina projekta komandu (atklātu no app/commands/), piemēram:

php runway migrate

Migrācijas ir SQL faili zem migrations/ (piemēram, YYYYMMDDHHMMSS_apraksts.sql SQLite un …_apraksts.mysql.sql MySQL), izvēlēti no jūsu datu bāzes draivera konfigurācijas / vides. Precīzi karodziņi un uzvedība ir noteikta šajā projekta komandā — palaidiet php runway migrate --help savā lietotnē.

AI palīgi

Runway atklāj AI orientētas komandas, kas tiek izmantotas ar AI un izstrādātāja pieredzi:

php runway ai:init
php runway ai:generate-instructions

Tās saglabā LLM akreditācijas datus un ģenerē projekta instrukcijas (galvenokārt AGENTS.md). Skeletonā uzskatiet AGENTS.md (un scoped kopijas zem app/) plus SECURITY.md par patiesības avotu aģentiem.

Visu maršrutu parādīšana

Tas parādīs visus maršrutus, kas pašlaik ir reģistrēti ar Flight.

php runway routes

Ja vēlaties skatīt tikai specifiskus maršrutus, varat nodot karodziņu maršrutu filtrēšanai.

# Parādīt tikai GET maršrutus
php runway routes --get

# Parādīt tikai POST maršrutus
php runway routes --post

# utt.

Pielāgotu komandu pievienošana Runway

Ja veidojat pakotni Flight vai vēlaties pievienot savas pielāgotās komandas savam projektam, varat to izdarīt, izveidojot src/commands/, flight/commands/, app/commands/ vai commands/ direktoriju savam projektam/pakotnei. Ja nepieciešama turpmāka pielāgošana, skatiet zemāk esošo sadaļu par konfigurāciju.

Skeletonā projekta komandas atrodas app/commands/ ar vārdu telpu App\Command. Runway tās atklāj pēc ceļa; saglabājiet šo mapi sinhronizētu ar Composer classmap/PSR-4, kā jūsu projekts jau dara.

Lai izveidotu komandu, vienkārši paplašiniet AbstractBaseCommand klasi un īstenojiet vismaz __construct metodi un execute metodi.

<?php

declare(strict_types=1);

namespace App\Command;

use flight\commands\AbstractBaseCommand;

class ExampleCommand extends AbstractBaseCommand
{
    /**
     * Konstruktor
     *
     * @param array<string,mixed> $config Konfigurācija no app/config/config.php
     */
    public function __construct(array $config)
    {
        parent::__construct('make:example', 'Izveidot piemēru dokumentācijai', $config);
        $this->argument('<funny-gif>', 'Smieklīgā gif nosaukums');
    }

    /**
     * Izpilda funkciju
     *
     * @return void
     */
    public function execute()
    {
        $io = $this->app()->io();

        $io->info('Veido piemēru...');

        // Dariet kaut ko šeit

        $io->ok('Piemērs izveidots!');
    }
}

Skatiet adhocore/php-cli dokumentāciju, lai iegūtu vairāk informācijas par to, kā izveidot savas pielāgotās komandas Flight lietotnē!

Konfigurācijas pārvaldība

Tā kā konfigurācija ir pārvietota uz app/config/config.php sākot ar v1.2.0, ir dažas palīgfunkcijas konfigurācijas pārvaldīšanai.

Skeleton padoms: Saglabājiet config.phpburtiskas PHP vērtības. Noslēpumi pieder .env. Izvairieties no $_ENV[...] izteiksmēm config.php iekšpusē — config:set pārraksta šo failu kā statiskus datus un varētu iebāzt noslēpumus failā. Skatiet Konfigurāciju.

Vecās konfigurācijas migrēšana

Ja jums ir vecs .runway-config.json fails, varat viegli migrēt to uz app/config/config.php ar šādu komandu:

php runway config:migrate

Konfigurācijas vērtības iestatīšana

Varat iestatīt konfigurācijas vērtību, izmantojot config:set komandu. Tas ir noderīgi, ja vēlaties atjaunināt konfigurācijas vērtību, neatverot failu.

php runway config:set app_root "app/"

Konfigurācijas vērtības iegūšana

Varat iegūt konfigurācijas vērtību, izmantojot config:get komandu.

php runway config:get app_root

Visas Runway konfigurācijas

Ja nepieciešams pielāgot Runway konfigurāciju, varat iestatīt šīs vērtības app/config/config.php. Zemāk ir dažas papildu konfigurācijas, kuras varat iestatīt:

<?php
// app/config/config.php
return [
    // ... citi konfigurācijas vērtības ...

    'runway' => [
        // Šeit atrodas jūsu lietotnes direktorija
        'app_root' => 'app/',

        // Šis ir direktorijs, kur atrodas jūsu root index fails
        'index_root' => 'public/',

        // Tie ir ceļi uz citu projektu saknēm
        'root_paths' => [
            '/home/user/different-project',
            '/var/www/another-project'
        ],

        // Bāzes ceļi visticamāk nav jākonfigurē, bet tas ir šeit, ja vēlaties
        'base_paths' => [
            '/includes/libs/vendor', // ja jums ir patiešām unikāls ceļš jūsu vendor direktorijai vai kaut kam citam
        ],

        // Galīgie ceļi ir atrašanās vietas projektā, kur meklēt komandu failus
        'final_paths' => [
            'src/diff-path/commands',
            'app/module/admin/commands',
        ],

        // Ja vēlaties vienkārši pievienot pilnu ceļu, dariet to (absolūts vai relatīvs pret projekta sakni)
        'paths' => [
            '/home/user/different-project/src/diff-path/commands',
            '/var/www/another-project/app/module/admin/commands',
            'app/my-unique-commands'
        ]
    ]
];

Konfigurācijas piekļuve

Ja nepieciešams efektīvi piekļūt konfigurācijas vērtībām, varat tām piekļūt caur __construct metodi vai app() metodi. Ir arī svarīgi atzīmēt, ka ja jums ir app/config/services.php fails, šie pakalpojumi arī būs pieejami jūsu komandai.

public function execute()
{
    $io = $this->app()->io();

    // Piekļūt konfigurācijai
    $app_root = $this->config['runway']['app_root'];

    // Piekļūt pakalpojumiem, piemēram, varbūt datu bāzes savienojumam
    $database = $this->config['database']

    // ...
}

AI palīgu ietvari

Runway ir daži palīgu ietvari, kas atvieglo AI komandu ģenerēšanu. Varat izmantot addOption un addArgument veidā, kas jutās līdzīgi Symfony Console. Tas ir noderīgi, ja izmantojat AI rīkus savu komandu ģenerēšanai.

public function __construct(array $config)
{
    parent::__construct('make:example', 'Izveidot piemēru dokumentācijai', $config);

    // Mode arguments ir nullable un noklusēti kā pilnībā neobligāti
    $this->addOption('name', 'Piemēra nosaukums', null);
}

Skatiet arī

Awesome-plugins/tracy_extensions

Tracy Flight Panel Paplašinājumi

Šis ir paplašinājumu komplekts, lai padarītu darbu ar Flight nedaudz bagātāku.

Tas ir īpaši noderīgi ar oficiālo skeletu, kurš pēc noklusējuma izmanto Twig: tas pats izkārtojums AI rīki seko arī skaidri parādās Tracy joslā.

Šis ir Panelis

Flight Bar

Un katrs panelis attēlo ļoti noderīgu informāciju par jūsu aplikāciju!

Flight Data Flight Database Flight Request

Klikšķiniet šeit lai skatītu kodu.

Instalācija

Izpildiet composer require flightphp/tracy-extensions --dev un jūs esat ceļā!

Twig nav stingra šī pakotnes atkarība. Instalējiet twig/twig tikai tad, ja vēlaties Twig paneli (skelets to jau dara skatiem).

Konfigurācija

Ir ļoti maz konfigurācijas, kas jums jāveic, lai to sāktu. Jums būs jāinicializē Tracy atkļūdotājs pirms šī lietošanas https://tracy.nette.org/en/guide:

<?php

use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;

// bootstrap kods
require __DIR__ . '/vendor/autoload.php';

Debugger::enable();
// Jums var būt nepieciešams norādīt savu vidi ar Debugger::enable(Debugger::DEVELOPMENT)

// ja savā app izmantojat datu bāzes savienojumus, ir 
// nepieciešams PDO wrapper, ko izmantot TIKAI IZSTRĀDES laikā (nevis ražošanā!)
// Tam ir tie paši parametri kā parastam PDO savienojumam
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// vai ja pievienojat to Flight framework
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// tagad, kad veicat vaicājumu, tas uztvers laiku, vaicājumu un parametrus

// Tas savieno punktus
if(Debugger::$showBar === true) {
    // Tam jābūt false vai Tracy nevar pat renderēt :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// vairāk koda

Flight::start();

Papildu Konfigurācija

Sesijas Dati

Ja jums ir pielāgots sesiju apstrādātājs (piemēram, ghostff/session), varat nodot jebkuru sesiju datu masīvu Tracy, un tas automātiski to izvadīs jums. Jūs to nododat ar session_data atslēgu otrajā parametrā TracyExtensionLoader konstruktorā.


use Ghostff\Session\Session;
// vai izmantot flight\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

if(Debugger::$showBar === true) {
    // Tam jābūt false vai Tracy nevar pat renderēt :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

// maršruti un citas lietas...

Flight::start();

Twig panelis (neobligāts)

Ja jūsu app izmanto Twig (ieskaitot oficiālo skeletu), varat parādīt veidnes metrikas Tracy joslā. Izveidojiet Twig Profile, pievienojiet ProfilerExtension savai videi, pēc tam nododiet šo profilu ielādētājam zem twig_profile atslēgas. Pievienojiet profilēšanu tikai izstrādes laikā.

<?php

use flight\debug\tracy\TracyExtensionLoader;
use flight\debug\tracy\TwigTracyExtension;
use Tracy\Debugger;
use Twig\Environment;
use Twig\Extension\ProfilerExtension;
use Twig\Loader\FilesystemLoader;
use Twig\Profiler\Profile;

$loader = new FilesystemLoader(__DIR__ . '/views');
$twig = new Environment($loader, [
    'debug' => true,
    'cache' => false,
]);

// Neobligāti: atklāt Tracy dump palīgfunkcijas veidnēs
// {{ dump(var) }}, {{ bdump(var) }}, {{ dumpe(var) }}
$twig->addExtension(new TwigTracyExtension());

$tracyConfig = [];
if (Debugger::$showBar === true) {
    $profile = new Profile();
    $twig->addExtension(new ProfilerExtension($profile));
    $tracyConfig['twig_profile'] = $profile;
}

if (Debugger::$showBar === true) {
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), $tracyConfig);
}

// Kartēt Flight::render() uz Twig (piemērs)
Flight::map('render', function (string $template, array $data = []) use ($twig) {
    if (substr($template, -5) !== '.twig') {
        $template .= '.twig';
    }
    echo $twig->render($template, $data);
});

Ko panelis parāda

Twig cilne ir paslēpta, kad vaicājumam netika renderētas veidnes, vai kad izlaižat twig_profile (vai jums nav Twig instalēts) — citi Flight paneļi turpina darboties.

Skeletveida services.php, izveidojiet to pašu $profile / ProfilerExtension, kad atkļūdošana ir ieslēgta, nododiet twig_profile uz TracyExtensionLoader, un turpiniet izmantot savu kopīgo Twig vidi priekš $app->render().

Latte

PHP 8.1+ ir nepieciešams šai sadaļai.

Ja jums ir Latte instalēts savā projektā, Tracy ir vietējā integrācija ar Latte, lai analizētu jūsu veidnes. Jūs vienkārši reģistrējat paplašinājumu ar savu Latte instanci (šī ir Latte paša Tracy tilts, nevis Twig panelis augšā).


require 'vendor/autoload.php';

$app = Flight::app();

$app->map('render', function($template, $data, $block = null) {
    $latte = new Latte\Engine;

    // citas konfigurācijas...

    // pievienot paplašinājumu tikai tad, ja Tracy Debug josla ir ieslēgta
    if(Debugger::$showBar === true) {
        // šeit jūs pievienojat Latte Panel Tracy
        $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
    }

    $latte->render($template, $data, $block);
});

Skatīt Arī

Awesome-plugins/apm

FlightPHP APM Dokumentācija

Laipni lūdzam FlightPHP APM — jūsu lietotnes personīgais veiktspējas treneris! Šī rokasgrāmata ir jūsu ceļvedis, kā iestatīt, izmantot un apgūt Lietotnes veiktspējas uzraudzību (APM) ar FlightPHP. Neatkarīgi no tā, vai meklējat lēnus pieprasījumus vai vēlaties iedziļināties latentuma diagrammās, mēs esam jums palīdzējuši. Padarīsim jūsu lietotni ātrāku, lietotājus laimīgākus un atkļūdošanas sesijas vieglākas!

Skatīt demo Flight Docs vietnes informācijas paneli.

FlightPHP APM

Kāpēc APM ir svarīgs

Iedomājieties: jūsu lietotne ir aizņemta restorāna. Bez veida, kā izsekot, cik ilgi pasūtījumi aizņem vai kur virtuve kavējas, jūs minat, kāpēc klienti aiziet neapmierināti. APM ir jūsu sous-chef — tas vēro katru soli, no ienākošajiem pieprasījumiem līdz datubāzes vaicājumiem, un atzīmē visu, kas jūs palēnina. Lēnas lapas zaudē lietotājus (pētījumi saka, ka 53% atstāj, ja vietne aizņem vairāk nekā 3 sekundes, lai ielādētu!), un APM palīdz jums noķert šīs problēmas pirms tās sāp. Tas ir proaktīvs miera prāts — mazāk “kāpēc tas ir salauzts?” brīžu, vairāk “skatieties, cik gludi tas darbojas!” uzvaru.

Uzstādīšana

Sāciet darbu ar Composer:

composer require flightphp/apm

Jums būs nepieciešams:

Atbalstītās datubāzes

FlightPHP APM pašlaik atbalsta šādas datubāzes metrikas glabāšanai:

Jūs varat izvēlēties savu datubāzes tipu konfigurācijas solī (skatīt zemāk). Pārliecinieties, ka jūsu PHP vidē ir instalēti nepieciešamie paplašinājumi (piem., pdo_sqlite vai pdo_mysql).

Darba sākšana

Šeit ir jūsu soli pa solim ceļvedis APM izcilībai:

1. Reģistrējiet APM

Ievietojiet šo savā index.php vai services.php failā, lai sāktu izsekošanu:

use flight\apm\logger\LoggerFactory;
use flight\database\SimplePdo;
use flight\Apm;

$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);

// If you're adding a database connection
// Prefer SimplePdo (or PdoQueryCapture from Tracy Extensions in dev).
// Enable APM query tracking via the options array (5th argument).
$pdo = new SimplePdo('mysql:host=localhost;dbname=example', 'user', 'pass', null, [
    'trackApmQueries' => true, // required to capture queries for the APM
]);
$Apm->addPdoConnection($pdo);

Kas šeit notiek?

Pro padoms: Paraugu ņemšana Ja jūsu lietotne ir aizņemta, katra pieprasījuma reģistrēšana var pārslodzi sistēmu. Izmantojiet parauga ātrumu (0.0 līdz 1.0):

$Apm = new Apm($ApmLogger, 0.1); // Logs 10% of requests

Tas saglabā veiktspēju ātru, vienlaikus nodrošinot jums stabilus datus.

2. Konfigurējiet to

Palaidiet šo, lai izveidotu savu .runway-config.json:

php vendor/bin/runway apm:init

Ko tas dara?

Šis process arī jautās, vai vēlaties palaist migrācijas šim iestatījumam. Ja iestatāt to pirmo reizi, atbilde ir jā.

Kāpēc divas atrašanās vietas? Neapstrādātās metrikas ātri uzkrājas (domājiet par nefiltrētiem žurnāliem). Strādnieks tās apstrādā strukturētā galamērķī informācijas panelim. Saglabā lietas sakārtotas!

3. Apstrādājiet metrikas ar strādnieku

Strādnieks pārvērš neapstrādātās metrikas informācijas panelim gatavos datos. Palaidiet to vienreiz:

php vendor/bin/runway apm:worker

Ko tas dara?

Saglabājiet to darboties Dzīvām lietotnēm jums būs nepieciešama nepārtraukta apstrāde. Šeit ir jūsu opcijas:

Kāpēc apgrūtināties? Bez strādnieka jūsu informācijas panelis ir tukšs. Tas ir tilts starp neapstrādātiem žurnāliem un darbības ieskatiem.

4. Palaidiet informācijas paneli

Skatiet savas lietotnes vitālos rādītājus:

php vendor/bin/runway apm:dashboard

Ko tas dara?

Pielāgojiet to:

php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php

Atveriet URL savā pārlūkprogrammā un izpētiet!

Ražošanas režīms

Ražošanai jums var nākties izmēģināt dažas metodes, lai palaistu informācijas paneli, jo tur droši vien ir ugunsmūri un citi drošības pasākumi. Šeit ir dažas opcijas:

Vēlaties citu informācijas paneli?

Jūs varat izveidot savu informācijas paneli, ja vēlaties! Skatieties vendor/flightphp/apm/src/apm/presenter direktoriju idejām, kā prezentēt datus savam informācijas panelim!

Informācijas paneļa funkcijas

Informācijas panelis ir jūsu APM HQ — šeit ir tas, ko redzēsiet:

Papildu:

Piemērs: Pieprasījums uz /users var parādīt:

Pielāgotu notikumu pievienošana

Izsekot jebko — kā API zvanu vai maksājuma procesu:

use flight\apm\CustomEvent;

$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('api_call', [
    'endpoint' => 'https://api.example.com/users',
    'response_time' => 0.25,
    'status' => 200
]));

Kur tas parādās? Informācijas paneļa pieprasījuma detaļās zem “Pielāgotie notikumi” — izvēršams ar skaistu JSON formatējumu.

Lietošanas gadījums:

$start = microtime(true);
$apiResponse = file_get_contents('https://api.example.com/data');
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('external_api', [
    'url' => 'https://api.example.com/data',
    'time' => microtime(true) - $start,
    'success' => $apiResponse !== false
]));

Tagad redzēsiet, vai šis API velk jūsu lietotni uz leju!

Datubāzes uzraudzība

Izsekot PDO vaicājumus šādi:

use flight\database\SimplePdo;

$pdo = new SimplePdo('sqlite:/path/to/db.sqlite', null, null, null, [
    'trackApmQueries' => true, // required to capture queries for the APM
]);
$Apm->addPdoConnection($pdo);

Ko jūs saņemat:

Uzmanību:

Piemēra izvade:

Strādnieka opcijas

Pielāgojiet strādnieku pēc savas patikas:

Piemērs:

php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600

Darbojas stundu, apstrādājot 100 metrikas vienlaikus.

Pieprasījuma ID lietotnē

Katram pieprasījumam ir unikāls pieprasījuma ID izsekošanai. Jūs varat izmantot šo ID savā lietotnē, lai korelētu žurnālus un metrikas. Piemēram, jūs varat pievienot pieprasījuma ID kļūdas lapai:

Flight::map('error', function($message) {
    // Get the request ID from the response header X-Flight-Request-Id
    $requestId = Flight::response()->getHeader('X-Flight-Request-Id');

    // Additionally you could fetch it from the Flight variable
    // This method won't work well in swoole or other async platforms.
    // $requestId = Flight::get('apm.request_id');

    echo "Error: $message (Request ID: $requestId)";
});

Jaunināšana

Ja jaunināt uz jaunāku APM versiju, pastāv iespēja, ka ir nepieciešamas datubāzes migrācijas. Jūs varat to izdarīt, palaižot šādu komandu:

php vendor/bin/runway apm:migrate

Tas palaidīs visas migrācijas, kas nepieciešamas, lai atjauninātu datubāzes shēmu uz jaunāko versiju.

Piezīme: Ja jūsu APM datubāze ir liela izmēra, šīs migrācijas var aizņemt kādu laiku. Jūs varat vēlēties palaist šo komandu ārpus maksimālās slodzes stundām.

Jaunināšana no 0.4.3 -> 0.5.0

Ja jaunināt no 0.4.3 uz 0.5.0, jums būs jāpalaiž šāda komanda:

php vendor/bin/runway apm:config-migrate

Tas migrēs jūsu konfigurāciju no vecā formāta, izmantojot .runway-config.json failu, uz jauno formātu, kas glabā atslēgu/vērtību config.php failā.

Veco datu tīrīšana

Lai saglabātu savu datubāzi sakārtotu, jūs varat iztīrīt vecos datus. Tas ir īpaši noderīgi, ja vadāt aizņemtu lietotni un vēlaties saglabāt datubāzes izmēru pārvaldāmu. Jūs varat to izdarīt, palaižot šādu komandu:

php vendor/bin/runway apm:purge

Tas noņems visus datus, kas vecāki par 30 dienām no datubāzes. Jūs varat pielāgot dienu skaitu, nododot citu vērtību --days opcijai:

php vendor/bin/runway apm:purge --days 7

Tas noņems visus datus, kas vecāki par 7 dienām no datubāzes.

Problēmu novēršana

Iestrēguši? Izmēģiniet šos:

Awesome-plugins/tracy

Tracy

Tracy ir brīnišķīgs kļūdu apstrādātājs, ko var izmantot kopā ar Flight. Tam ir vairāki paneļi, kas var palīdzēt atkļūdot jūsu lietojumprogrammu. Tas ir arī ļoti viegli paplašināms un pievienot savus paneļus. Flight komanda ir izveidojusi dažus paneļus īpaši Flight projektiem ar flightphp/tracy-extensions spraudni (Flight mainīgie, DB vaicājumi, pieprasījums, sesija un izvēles Twig panelis, kad nododat profilētāja profilu — skatiet Tracy Extensions).

Instalācija

Instalējiet ar composer. Un jūs patiešām vēlēsieties to instalēt bez izstrādes versijas, jo Tracy nāk ar ražošanas kļūdu apstrādes komponenti.

composer require tracy/tracy

Pamata konfigurācija

Ir dažas pamata konfigurācijas iespējas, lai sāktu darbu. Vairāk par tām varat lasīt Tracy dokumentācijā.


require 'vendor/autoload.php';

use Tracy\Debugger;

// Iespējot Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // dažreiz jums ir jābūt precīzam (arī Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // jūs varat arī norādīt IP adrešu masīvu

// Šeit tiks reģistrētas kļūdas un izņēmumi. Pārliecinieties, ka šis direktorijs eksistē un ir rakstāms.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // rādīt visas kļūdas
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // visas kļūdas izņemot novecojušus paziņojumus
if (Debugger::$showBar) {
    $app->set('flight.content_length', false); // ja Debugger josla ir redzama, tad content-length nevar iestatīt ar Flight

    // Tas ir specifiski Tracy Extension for Flight, ja esat to iekļāvis
    // pretējā gadījumā komentējiet to.
    new TracyExtensionLoader($app);
}

Noderīgi padomi

Kad atkļūdojat savu kodu, ir dažas ļoti noderīgas funkcijas datu izvadīšanai.

Awesome-plugins/active_record

Flight Active Record

Aktīvs ieraksts ir datubāzes entītijas kartēšana uz PHP objektu. Vienkārši izteikts, ja jums ir lietotāju tabula datubāzē, jūs varat "tulkot" rindas šajā tabulā uz User klasi un $user objektu jūsu koda bāzē. Skatiet pamatinstanci.

Noklikšķiniet šeit, lai iegūtu repozitoriju GitHub.

Pamatinstance

Pieņemsim, ka jums ir šāda tabula:

CREATE TABLE users (
    id INTEGER PRIMARY KEY, 
    name TEXT, 
    password TEXT 
);

Tagad jūs varat izveidot jaunu klasi, lai attēlotu šo tabulu:

/**
 * ActiveRecord klase parasti ir vienskaitlī
 * 
 * Ļoti ieteicams pievienot tabulas īpašības kā komentārus šeit
 * 
 * @property int    $id
 * @property string $name
 * @property string $password
 */ 
class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        // jūs varat iestatīt to šādi
        parent::__construct($database_connection, 'users');
        // vai šādi
        parent::__construct($database_connection, null, [ 'table' => 'users']);
    }
}

Tagad vērojiet, kā notiek burvība!

// sqlite gadījumā
$database_connection = new PDO('sqlite:test.db'); // tas ir tikai piemēram, jūs droši vien izmantosiet īstu datubāzes savienojumu

// mysql gadījumā
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');

// vai mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// vai mysqli ar neobjekta bāzētu izveidi
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');

$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('some cool password');
$user->insert();
// vai $user->save();

echo $user->id; // 1

$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// šeit nevar izmantot $user->save(), pretējā gadījumā tas domās, ka tas ir atjauninājums!

echo $user->id; // 2

Un tik viegli bija pievienot jaunu lietotāju! Tagad, kad datubāzē ir lietotāja rinda, kā jūs to izvilksiet?

$user->find(1); // atrast id = 1 datubāzē un atgriezt to.
echo $user->name; // 'Bobby Tables'

Un kas, ja jūs vēlaties atrast visus lietotājus?

$users = $user->findAll();

Ko darīt ar noteiktu nosacījumu?

$users = $user->like('name', '%mamma%')->findAll();

Redziet, cik jautri tas ir? Instalēsim to un sāksim!

Instalēšana

Vienkārši instalējiet ar Composer

composer require flightphp/active-record 

Lietošana

To var izmantot kā neatkarīgu bibliotēku vai ar Flight PHP Framework. Pilnībā atkarīgs no jums.

Neatkarīgi

Vienkārši pārliecinieties, ka konstruktoram nododat PDO savienojumu.

$pdo_connection = new PDO('sqlite:test.db'); // tas ir tikai piemēram, jūs droši vien izmantosiet īstu datubāzes savienojumu

$User = new User($pdo_connection);

Vai nevēlaties vienmēr iestatīt datubāzes savienojumu konstruktorā? Skatiet Datubāzes savienojuma pārvaldību citiem variantiem!

Reģistrēšana kā metode Flight

Ja izmantojat Flight PHP Framework, jūs varat reģistrēt ActiveRecord klasi kā servisu, bet patiesībā nav obligāti.

Flight::register('user', 'User', [ $pdo_connection ]);

// tad jūs varat izmantot to šādi kontrolierī, funkcijā utt.

Flight::user()->find(1);

runway Metodes

runway ir CLI rīks Flight, kas ir ar pielāgotu komandu šai bibliotēkai.

# Lietošana
php runway make:record database_table_name [class_name]

# Piemērs
php runway make:record users

Tas izveidos jaunu klasi app/records/ direktorijā kā UserRecord.php ar šādu saturu:

<?php

declare(strict_types=1);

namespace app\records;

/**
 * ActiveRecord klase lietotāju tabulai.
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 *
 * @property int $id
 * @property string $username
 * @property string $email
 * @property string $password_hash
 * @property string $created_dt
 */
class UserRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations Iestatīt modeļa attiecības
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [
        // 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
    ];

    /**
     * Konstruktors
     * @param mixed $databaseConnection Savienojums ar datubāzi
     */
    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'users');
    }
}

CRUD funkcijas

find($id = null) : boolean|ActiveRecord

Atrast vienu ierakstu un piešķirt to pašreizējam objektam. Ja nododat kādu $id, tas veiks meklēšanu primārajā atslēgā ar šo vērtību. Ja nekas netiek nodots, tas atradīs pirmo ierakstu tabulā.

Turklāt jūs varat nodot citas palīgmēģenes, lai vaicātu tabulu.

// atrast ierakstu ar dažiem nosacījumiem iepriekš
$user->notNull('password')->orderBy('id DESC')->find();

// atrast ierakstu pēc specifiska id
$id = 123;
$user->find($id);

findAll(): array<int,ActiveRecord>

Atrast visus ierakstus tabulā, ko jūs norādāt.

$user->findAll();

isHydrated(): boolean (v0.4.0)

Atgriež true, ja pašreizējais ieraksts ir hidratēts (iegūts no datubāzes).

$user->find(1);
// ja ieraksts ir atrasts ar datiem...
$user->isHydrated(); // true

insert(): boolean|ActiveRecord

Ievieto pašreizējo ierakstu datubāzē.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Teksta bāzētas primārās atslēgas

Ja jums ir teksta bāzēta primārā atslēga (piemēram, UUID), jūs varat iestatīt primārās atslēgas vērtību pirms ievietošanas divos veidos.

$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // vai $user->save();

vai jūs varat ļaut primārajai atslēgai automātiski ģenerēties caur notikumiem.

class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
        // jūs varat iestatīt primaryKey arī šādi, nevis ar masīvu iepriekš.
        $this->primaryKey = 'uuid';
    }

    protected function beforeInsert(self $self) {
        $self->uuid = uniqid(); // vai kā jūs ģenerējat savas unikālās id
    }
}

Ja jūs neiestatāt primāro atslēgu pirms ievietošanas, tā tiks iestatīta uz rowid un datubāze to ģenerēs jums, bet tā nepastāvēs, jo tas lauks var nepastāvēt jūsu tabulā. Tāpēc ieteicams izmantot notikumu, lai automātiski apstrādātu to.

update(): boolean|ActiveRecord

Atjaunina pašreizējo ierakstu datubāzē.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();

save(): boolean|ActiveRecord

Ievieto vai atjaunina pašreizējo ierakstu datubāzē. Ja ierakstam ir id, tas atjauninās, citādi ievietos.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();

Piezīme: Ja jums ir definētas attiecības klasē, tas rekursīvi saglabās tās attiecības, ja tās ir definētas, instance un ir netīri dati atjaunināšanai. (v0.4.0 un augstāk)

delete(): boolean

Dzēš pašreizējo ierakstu no datubāzes.

$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();

Jūs varat arī dzēst vairākus ierakstus, izpildot meklēšanu iepriekš.

$user->like('name', 'Bob%')->delete();

dirty(array $dirty = []): ActiveRecord

Netīri dati attiecas uz datiem, kas ir mainīti ierakstā.

$user->greaterThan('id', 0)->orderBy('id desc')->find();

// līdz šim nekas nav "netīrs".

$user->email = 'test@example.com'; // tagad email tiek uzskatīts par "netīru", jo tas ir mainīts.
$user->update();
// tagad nav datu, kas ir netīri, jo tie ir atjaunināti un saglabāti datubāzē

$user->password = password_hash()'newpassword'); // tagad tas ir netīrs
$user->dirty(); // neko nepadojot, tas notīrīs visus netīros ierakstus.
$user->update(); // nekas netiks atjaunināts, jo nekas netika uztverts kā netīrs.

$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // gan name, gan password tiks atjaunināti.

copyFrom(array $data): ActiveRecord (v0.4.0)

Tas ir alias dirty() metodei. Tas ir nedaudz skaidrāks, ko jūs darāt.

$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // gan name, gan password tiks atjaunināti.

isDirty(): boolean (v0.4.0)

Atgriež true, ja pašreizējais ieraksts ir mainīts.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true

reset(bool $include_query_data = true): ActiveRecord

Atiestata pašreizējo ierakstu uz tā sākotnējo stāvokli. Tas ir patiešām labs lietošanai cilpas veida uzvedībās. Ja padojat true, tas arī atiestatīs vaicājuma datus, kas tika izmantoti, lai atrastu pašreizējo objektu (noklusējuma uzvedība).

$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);

foreach($users as $user) {
    $user_company->reset(); // sākt ar tīru lapu
    $user_company->user_id = $user->id;
    $user_company->company_id = $some_company_id;
    $user_company->insert();
}

getBuiltSql(): string (v0.4.1)

Pēc tam, kad izpildāt find(), findAll(), insert(), update() vai save() metodi, jūs varat iegūt SQL, kas tika izveidots, un izmantot to atkļūdošanas nolūkos.

SQL vaicājuma metodes

select(string $field1 [, string $field2 ... ])

Jūs varat atlasīt tikai dažas kolonnas tabulā, ja vēlaties (tas ir efektīvāks patiešām plašās tabulās ar daudzām kolonnām)

$user->select('id', 'name')->find();

from(string $table)

Jūs tehniski varat izvēlēties arī citu tabulu! Kāpēc gan ne?!

$user->select('id', 'name')->from('user')->find();

join(string $table_name, string $join_condition)

Jūs pat varat pievienoties citai tabulai datubāzē.

$user->join('contacts', 'contacts.user_id = users.id')->find();

where(string $where_conditions)

Jūs varat iestatīt dažus pielāgotus where argumentus (jūs nevarat iestatīt parametrus šajā where paziņojumā)

$user->where('id=1 AND name="demo"')->find();

Drošības piezīme - Jūs varētu būt kārdināts darīt kaut ko līdzīgu $user->where("id = '{$id}' AND name = '{$name}'")->find();. Lūdzu, NEDARIET TO!!! Tas ir pakļauts tam, ko sauc par SQL injekcijas uzbrukumiem. Ir daudz rakstu tiešsaistē, lūdzu, meklējiet Google "sql injection attacks php" un atradīsit daudz rakstu par šo tēmu. Pareizais veids, kā apstrādāt to ar šo bibliotēku, ir, nevis izmantot šo where() metodi, bet gan kaut ko līdzīgu $user->eq('id', $id)->eq('name', $name)->find(); Ja jums absolūti jāto dara, PDO bibliotēkai ir $pdo->quote($var), lai aizbēgtu to jums. Tikai pēc quote() izmantošanas jūs varat izmantot to where() paziņojumā.

group(string $group_by_statement)/groupBy(string $group_by_statement)

Grupēt jūsu rezultātus pēc noteikta nosacījuma.

$user->select('COUNT(*) as count')->groupBy('name')->findAll();

order(string $order_by_statement)/orderBy(string $order_by_statement)

Kārtot atgriezto vaicājumu noteiktā veidā.

$user->orderBy('name DESC')->find();

limit(string $limit)/limit(int $offset, int $limit)

Ierobežot atgriezto ierakstu skaitu. Ja dots otrais int, tas būs nobīde, ierobežojums tieši kā SQL.

$user->orderby('name DESC')->limit(0, 10)->findAll();

WHERE nosacījumi

equal(string $field, mixed $value) / eq(string $field, mixed $value)

Kur field = $value

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

Kur field <> $value

$user->ne('id', 1)->find();

isNull(string $field)

Kur field IS NULL

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

Kur field IS NOT NULL

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

Kur field > $value

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

Kur field < $value

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

Kur field >= $value

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

Kur field <= $value

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

Kur field LIKE $value vai field NOT LIKE $value

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

Kur field IN($value) vai field NOT IN($value)

$user->in('id', [1, 2])->find();

between(string $field, array $values)

Kur field BETWEEN $value AND $value1

$user->between('id', [1, 2])->find();

OR nosacījumi

Ir iespējams apvijināt jūsu nosacījumus OR paziņojumā. Tas tiek darīts ar startWrap() un endWrap() metodi vai aizpildot 3. parametru nosacījumā pēc lauka un vērtības.

// Metode 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Tas tiks novērtēts kā `id = 1 AND (name = 'demo' OR name = 'test')`

// Metode 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Tas tiks novērtēts kā `id = 1 OR name = 'demo'`

Attiecības

Jūs varat iestatīt vairākas attiecību veidus, izmantojot šo bibliotēku. Jūs varat iestatīt one->many un one->one attiecības starp tabulām. Tas prasa nedaudz papildu iestatījumu klasē iepriekš.

Iestatot $relations masīvu nav grūti, bet pareizās sintakses minēšana var būt mulsinoša.

protected array $relations = [
    // jūs varat nosaukt atslēgu jebkā vēlaties. ActiveRecord nosaukums droši vien ir labs. Piem: user, contact, client
    'user' => [
        // obligāti
        // self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
        self::HAS_ONE, // tas ir attiecību veids

        // obligāti
        'Some_Class', // tas ir "cits" ActiveRecord klase, uz kuru tas atsaucas

        // obligāti
        // atkarībā no attiecību veida
        // self::HAS_ONE = ārējā atslēga, kas atsaucas uz savienojumu
        // self::HAS_MANY = ārējā atslēga, kas atsaucas uz savienojumu
        // self::BELONGS_TO = lokālā atslēga, kas atsaucas uz savienojumu
        'local_or_foreign_key',
        // tikai FYI, tas arī pievienojas tikai uz "citas" modeļa primāro atslēgu

        // izvēles
        [ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // papildu nosacījumi, ko vēlaties, pievienojoties attiecībai
        // $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))

        // izvēles
        'back_reference_name' // tas ir, ja vēlaties atpakaļatsauce uz šo attiecību atpakaļ uz sevi Piem: $user->contact->user;
    ];
]
class User extends ActiveRecord{
    protected array $relations = [
        'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
        'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
    ];

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }
}

class Contact extends ActiveRecord{
    protected array $relations = [
        'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
        'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
    ];
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'contacts');
    }
}

Tagad mums ir atsauces iestatītas, lai mēs varētu izmantot tās ļoti viegli!

$user = new User($pdo_connection);

// atrast jaunāko lietotāju.
$user->notNull('id')->orderBy('id desc')->find();

// iegūt kontaktus, izmantojot attiecību:
foreach($user->contacts as $contact) {
    echo $contact->id;
}

// vai mēs varam iet otru ceļu.
$contact = new Contact();

// atrast vienu kontaktu
$contact->find();

// iegūt lietotāju, izmantojot attiecību:
echo $contact->user->name; // tas ir lietotāja vārds

Diezgan forši, vai ne?

Eager Loading

Pārskats

Eager loading atrisina N+1 vaicājuma problēmu, iepriekš ielādējot attiecības. Tā vietā, lai izpildītu atsevišķu vaicājumu katrai ieraksta attiecībai, eager loading iegūst visus saistītos datus tikai vienā papildu vaicājumā uz attiecību.

Piezīme: Eager loading ir pieejams tikai v0.7.0 un augstāk.

Pamatlietošana

Izmantojiet with() metodi, lai norādītu, kuras attiecības eager ielādēt:

// Ielādēt lietotājus ar viņu kontaktiem 2 vaicājumos, nevis N+1
$users = $user->with('contacts')->findAll();
foreach ($users as $u) {
    foreach ($u->contacts as $contact) {
        echo $contact->email; // Nav papildu vaicājuma!
    }
}

Vairākas attiecības

Ielādēt vairākas attiecības uzreiz:

$users = $user->with(['contacts', 'profile', 'settings'])->findAll();

Attiecību veidi

HAS_MANY
// Eager ielādēt visus kontaktus katram lietotājam
$users = $user->with('contacts')->findAll();
foreach ($users as $u) {
    // $u->contacts jau ir ielādēts kā masīvs
    foreach ($u->contacts as $contact) {
        echo $contact->email;
    }
}
HAS_ONE
// Eager ielādēt vienu kontaktu katram lietotājam
$users = $user->with('contact')->findAll();
foreach ($users as $u) {
    // $u->contact jau ir ielādēts kā objekts
    echo $u->contact->email;
}
BELONGS_TO
// Eager ielādēt vecāku lietotājus visiem kontaktiem
$contacts = $contact->with('user')->findAll();
foreach ($contacts as $c) {
    // $c->user jau ir ielādēts
    echo $c->user->name;
}
Ar find()

Eager loading darbojas ar findAll() un find() :

$user = $user->with('contacts')->find(1);
// Lietotājs un visi viņu kontakti ielādēti 2 vaicājumos

Veiktspējas priekšrocības

Bez eager loading (N+1 problēma):

$users = $user->findAll(); // 1 vaicājums
foreach ($users as $u) {
    $contacts = $u->contacts; // N vaicājumi (viens uz lietotāju!)
}
// Kopā: 1 + N vaicājumi

Ar eager loading:

$users = $user->with('contacts')->findAll(); // 2 vaicājumi kopā
foreach ($users as $u) {
    $contacts = $u->contacts; // 0 papildu vaicājumi!
}
// Kopā: 2 vaicājumi (1 lietotājiem + 1 visiem kontaktiem)

10 lietotājiem tas samazina vaicājumus no 11 līdz 2 - 82% samazinājums!

Svarīgas piezīmes

Ierobežojumi

Pielāgota datu iestatīšana

Dažreiz jums var būt nepieciešams pievienot kaut ko unikālu jūsu ActiveRecord, piemēram, pielāgotu aprēķinu, kas varētu būt vieglāk pievienot objektam, kas tad tiks nodots, teiksim, šablonam.

setCustomData(string $field, mixed $value)

Jūs pievienojat pielāgoto datu ar setCustomData() metodi.

$user->setCustomData('page_view_count', $page_view_count);

Un tad jūs vienkārši atsaucaties uz to kā uz normālu objekta īpašību.

echo $user->page_view_count;

Notikumi

Viens vairāk super lielisks elements par šo bibliotēku ir par notikumiem. Notikumi tiek izraisīti noteiktos laikos, balstoties uz noteiktām metodēm, ko jūs saucat. Tie ir ļoti noderīgi, lai automātiski iestatītu datus jums.

onConstruct(ActiveRecord $ActiveRecord, array &config)

Tas ir patiešām noderīgi, ja jums ir nepieciešams iestatīt noklusējuma savienojumu vai kaut ko tādu.

// index.php vai bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

//
//
//

// User.php
class User extends flight\ActiveRecord {

    protected function onConstruct(self $self, array &$config) { // neaizmirstiet & atsauci
        // jūs varētu to darīt, lai automātiski iestatītu savienojumu
        $config['connection'] = Flight::db();
        // vai to
        $self->transformAndPersistConnection(Flight::db());

        // Jūs varat arī iestatīt tabulas nosaukumu šādi.
        $config['table'] = 'users';
    } 
}

beforeFind(ActiveRecord $ActiveRecord)

Tas, visticamāk, ir noderīgi tikai tad, ja jums ir nepieciešama vaicājuma manipulācija katru reizi.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFind(self $self) {
        // vienmēr palaidiet id >= 0, ja tas ir jūsu stils
        $self->gte('id', 0); 
    } 
}

afterFind(ActiveRecord $ActiveRecord)

Šis, visticamāk, ir noderīgāks, ja jums vienmēr ir jāpalaid kaut kāda loģika katru reizi, kad šis ieraksts tiek iegūts. Vai jums ir jāšifrē kaut kas? Vai jums ir jāpalaid pielāgots skaita vaicājums katru reizi (nav efektīvi, bet nu labi)?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFind(self $self) {
        // šifrēšana kaut kā
        $self->secret = yourDecryptFunction($self->secret, $some_key);

        // varbūt saglabājot kaut ko pielāgotu kā vaicājumu???
        $self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']; 
    } 
}

beforeFindAll(ActiveRecord $ActiveRecord)

Tas, visticamāk, ir noderīgi tikai tad, ja jums ir nepieciešama vaicājuma manipulācija katru reizi.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFindAll(self $self) {
        // vienmēr palaidiet id >= 0, ja tas ir jūsu stils
        $self->gte('id', 0); 
    } 
}

afterFindAll(array<int,ActiveRecord> $results)

Līdzīgs afterFind(), bet jūs varat to darīt visiem ierakstiem!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFindAll(array $results) {

        foreach($results as $self) {
            // dariet kaut ko foršu kā afterFind()
        }
    } 
}

beforeInsert(ActiveRecord $ActiveRecord)

Patiešām noderīgi, ja jums ir nepieciešams iestatīt noklusējuma vērtības katru reizi.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // iestatīt dažus labus noklusējumus
        if(!$self->created_date) {
            $self->created_date = gmdate('Y-m-d');
        }

        if(!$self->password) {
            $self->password = password_hash((string) microtime(true));
        }
    } 
}

afterInsert(ActiveRecord $ActiveRecord)

Varbūt jums ir gadījums, kad mainīt datus pēc ievietošanas?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // dariet, ko vēlaties
        Flight::cache()->set('most_recent_insert_id', $self->id);
        // vai ko citu....
    } 
}

beforeUpdate(ActiveRecord $ActiveRecord)

Patiešām noderīgi, ja jums ir nepieciešams iestatīt noklusējuma vērtības katru reizi atjauninājumā.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // iestatīt dažus labus noklusējumus
        if(!$self->updated_date) {
            $self->updated_date = gmdate('Y-m-d');
        }
    } 
}

afterUpdate(ActiveRecord $ActiveRecord)

Varbūt jums ir gadījums, kad mainīt datus pēc atjaunināšanas?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // dariet, ko vēlaties
        Flight::cache()->set('most_recently_updated_user_id', $self->id);
        // vai ko citu....
    } 
}

beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)

Tas ir noderīgi, ja vēlaties, lai notikumi notiktu gan ievietošanas, gan atjauninājuma laikā. Es jūs saīsināšu no gara skaidrojuma, bet esmu pārliecināts, ka jūs varat uzminēt, kas tas ir.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeSave(self $self) {
        $self->last_updated = gmdate('Y-m-d H:i:s');
    } 
}

beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)

Neesmu pārliecināts, ko jūs vēlētos darīt šeit, bet bez tiesājumiem! Dodieties!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeDelete(self $self) {
        echo 'Viņš bija drosmīgs karavīrs... :cry-face:';
    } 
}

Datubāzes savienojuma pārvaldība

Izmantojot šo bibliotēku, jūs varat iestatīt datubāzes savienojumu dažādos veidos. Jūs varat iestatīt savienojumu konstruktorā, varat iestatīt to caur konfigurācijas mainīgo $config['connection'] vai varat iestatīt to caur setDatabaseConnection() (v0.4.1).

$pdo_connection = new PDO('sqlite:test.db'); // piemēram
$user = new User($pdo_connection);
// vai
$user = new User(null, [ 'connection' => $pdo_connection ]);
// vai
$user = new User();
$user->setDatabaseConnection($pdo_connection);

Ja vēlaties izvairīties no $database_connection iestatīšanas katru reizi, kad saucat active record, ir veidi ap to!

// index.php vai bootstrap.php
// Iestatīt to kā reģistrētu klasi Flight
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

// User.php
class User extends flight\ActiveRecord {

    public function __construct(array $config = [])
    {
        $database_connection = $config['connection'] ?? Flight::db();
        parent::__construct($database_connection, 'users', $config);
    }
}

// Un tagad nav nepieciešami argumenti!
$user = new User();

Piezīme: Ja plānojat veikt unit testēšanu, darot to šādi, tas var pievienot dažus izaicinājumus unit testēšanai, bet kopumā, jo jūs varat injicēt savu savienojumu ar setDatabaseConnection() vai $config['connection'], tas nav pārāk slikti.

Ja jums ir jāatjauno datubāzes savienojums, piemēram, ja palaižat ilgu CLI skriptu un ir jāatjauno savienojums ik pa laikam, jūs varat atkārtoti iestatīt savienojumu ar $your_record->setDatabaseConnection($pdo_connection).

Iesaiste

Lūdzu, dariet. :D

Iestatīšana

Kad jūs iesaistāties, pārliecinieties, ka palaižat composer test-coverage, lai uzturētu 100% testa pārklājumu (tas nav īsts unit testa pārklājums, vairāk kā integrācijas testēšana).

Arī pārliecinieties, ka palaižat composer beautify un composer phpcs, lai labotu jebkādas linting kļūdas.

Licence

MIT

Awesome-plugins/latte

Latte

Latte ir pilnvērtīgs veidņu dz motor, kas ir ļoti viegli lietojams un jūtas tuvāk PHP sintaksei nekā Twig vai Smarty. To ir arī ļoti viegli paplašināt un pievienot savus filtrus un funkcijas.

Instalācija

Instalējiet ar composer.

composer require latte/latte

Pamata konfigurācija

Ir dažas pamata konfigurācijas opcijas, lai sāktu. Jūs varat lasīt vairāk par tām Latte dokumentācijā.


require 'vendor/autoload.php';

$app = Flight::app();

$app->map('render', function(string $template, array $data, ?string $block): void {
    $latte = new Latte\Engine;

    // Kur latte specifiski glabā savu kešu
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    $finalPath = Flight::get('flight.views.path') . $template;

    $latte->render($finalPath, $data, $block);
});

Vienkāršs izkārtojuma piemērs

Šeit ir vienkāršs izkārtojuma faila piemērs. Šis ir fails, kas tiks izmantots, lai aptvertu visas jūsu citas skatus.

<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
    <head>
        <title>{$title ? $title . ' - '}My App</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <nav>
                <!-- jūsu navigācijas elementi šeit -->
            </nav>
        </header>
        <div id="content">
            <!-- Šī ir maģija šeit -->
            {block content}{/block}
        </div>
        <div id="footer">
            &copy; Copyright
        </div>
    </body>
</html>

Un tagad mums ir jūsu fails, kas tiks renderēts tajā satura blokā:

<!-- app/views/home.latte -->
<!-- Tas pasaka Latte, ka šis fails ir "iekšā" layout.latte failā -->
{extends layout.latte}

<!-- Šis ir saturs, kas tiks renderēts izkārtojumā satura blokā -->
{block content}
    <h1>Sākumlapa</h1>
    <p>Sveiki manā lietotnē!</p>
{/block}

Pēc tam, kad jūs dodaties renderēt to savā funkcijā vai kontrolierī, jūs darītu kaut ko šādu:

// vienkāršs maršruts
Flight::route('/', function () {
    Flight::render('home.latte', [
        'title' => 'Sākumlapa'
    ]);
});

// vai ja jūs izmantojat kontrollieri
Flight::route('/', [HomeController::class, 'index']);

// HomeController.php
class HomeController
{
    public function index()
    {
        Flight::render('home.latte', [
            'title' => 'Sākumlapa'
        ]);
    }
}

Skatiet Latte dokumentāciju, lai iegūtu vairāk informācijas par to, kā izmantot Latte pilnā potenciālā!

Kļūdu labošana ar Tracy

PHP 8.1+ ir nepieciešams šai sadaļai.

Jūs varat izmantot arī Tracy, lai palīdzētu ar jūsu Latte veidņu failu kļūdu labošanu tieši no kastes! Ja jums jau ir instalēts Tracy, jums jāpievieno Latte paplašinājums Tracy.

// services.php
use Tracy\Debugger;

$app->map('render', function(string $template, array $data, ?string $block): void {
    $latte = new Latte\Engine;

    // Kur latte specifiski glabā savu kešu
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    $finalPath = Flight::get('flight.views.path') . $template;

    // Tas pievienos paplašinājumu tikai tad, ja ir iespējota Tracy atkļūdošanas josla
    if (Debugger::$showBar === true) {
        // šeit jūs pievienojat Latte paneli Tracy
        $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
    }
    $latte->render($finalPath, $data, $block);
});

Awesome-plugins/awesome_plugins

Brīnišķīgi spraudņi

Flight ir neticami paplašināms. Ir pieejami vairāki spraudņi, kurus var izmantot, lai pievienotu funkcionalitāti Flight lietotnei. Daži no tiem ir oficiāli atbalstīti Flight komandas, bet citi ir mikro/viegli risinājumi, kas palīdz sākt darbu.

MI rīki

Flight var padarīt vēl foršāku ar MI darbināmiem spraudņiem.

API dokumentācija

API dokumentācija ir būtiska jebkurai API. Tas palīdz izstrādātājiem saprast, kā mijiedarboties ar API un ko sagaidīt pretī. Ir pieejami daži rīki, kas palīdz ģenerēt API dokumentāciju Flight projektiem.

Lietotnes veiktspējas uzraudzība (APM)

Lietotnes veiktspējas uzraudzība (APM) ir būtiska jebkurai lietotnei. Tas palīdz izprast, kā darbojas lietotne un kur ir sastrēgumi. Ir pieejami vairāki APM rīki, kurus var izmantot ar Flight.

Asinhronā apstrāde

Flight jau ir ātrs ietvars, bet turbodzinēja pievienošana padara visu vēl aizraujošāku (un izaicināmāku)!

Autorizācija/Pieejas tiesības

Autorizācija un pieejas tiesības ir būtiskas jebkurai lietotnei, kurai nepieciešamas kontroles, lai noteiktu, kurš var piekļūt kam.

Autentifikācija

Autentifikācija ir būtiska lietotnēm, kurām nepieciešams verificēt lietotāja identitāti un nodrošināt API galapunktus.

Kešatmiņa

Kešatmiņa ir lielisks veids, kā paātrināt lietotni. Ir pieejamas vairākas kešatmiņas bibliotēkas, kuras var izmantot ar Flight.

CLI

CLI lietotnes ir lielisks veids, kā mijiedarboties ar lietotni. Jūs varat tās izmantot, lai ģenerētu kontrolierus, parādītu visus maršrutus un vairāk.

Sīkdatnes

Sīkdatnes ir lielisks veids, kā saglabāt nelielus datu fragmentus klienta pusē. Tās var izmantot, lai saglabātu lietotāja preferences, lietotnes iestatījumus un vairāk.

Atkļūdošana

Atkļūdošana ir būtiska, kad izstrādājat lokālajā vidē. Ir daži spraudņi, kas var uzlabot jūsu atkļūdošanas pieredzi.

Datubāzes

Datubāzes ir lielākās daļas lietotņu kodols. Tā ir vieta, kur saglabāt un iegūt datus. Dažas datubāzes bibliotēkas ir vienkārši aploki vaicājumu rakstīšanai, bet dažas ir pilnvērtīgas ORM sistēmas.

Šifrēšana

Šifrēšana ir būtiska jebkurai lietotnei, kas saglabā sensitīvus datus. Datus šifrēt un atšifrēt nav īpaši grūti, bet pareizi glabāt šifrēšanas atslēgu var būt sarežģīti. Vissvarīgākais ir nekad neglabāt šifrēšanas atslēgu publiskajā direktorijā vai to commit'ot kodā.

Darbu rinda

Darbu rindas ir patiešām noderīgas, lai asinhroni apstrādātu uzdevumus. Tas var būt e-pasta sūtīšana, attēlu apstrāde vai jebkas, kas nav jādara reālā laikā.

Sesija

Sesijas īsti nav noderīgas API, bet tīmekļa lietotnes izveidei sesijas var būt būtiskas stāvokļa un pieteikšanās informācijas uzturēšanai.

Veidnes

Veidnes ir jebkuras tīmekļa lietotnes ar lietotāja saskarni kodols. Ir pieejami vairāki veidņu dzinēji, kurus var izmantot ar Flight.

WordPress integrācija

Vai vēlaties izmantot Flight WordPress projektā? Tam ir ērts spraudnis!

Ieguldījums

Vai jums ir spraudnis, ko vēlaties dalīties? Iesniedziet pull request, lai to pievienotu sarakstam!

Media

Media

Mēs esam mēģinājuši izsekot dažādu veidu medijiem internetā par Flight. Skatiet zemāk dažādus resursus, kurus varat izmantot, lai uzzinātu vairāk par Flight.

Raksti un apraksti

Video un pamācības

Trūkst kaut kas?

Vai mums trūkst kaut kas, ko jūs esat uzrakstījis vai ierakstījis? Informējiet mūs ar problēmas ziņojumu vai pull request!

Examples

Vai ātri sākt?

Jums ir divas opcijas, lai sāktu darbu ar jaunu Flight projektu:

Kopienas sniegtie piemēri:

Vai vajag iedvesmu?

Lai gan šie nav oficiāli atbalstīti no Flight komandas puses, tie var dot jums idejas, kā strukturēt savus pašu projektus, kas izveidoti ar Flight!

Vai vēlaties Dalīties ar Savu Piemēru?

Ja jums ir projekts, kuru vēlaties dalīt, lūdzu, iesniedziet pull request, lai to pievienotu šim sarakstam!

Install/install

Instalēšanas instrukcijas

Pirms varat instalēt Flight, ir daži pamata priekšnosacījumi. Proti, jums būs nepieciešams:

  1. Instalēt PHP savā sistēmā
  2. Instalēt Composer, lai iegūtu vislabāko izstrādātāja pieredzi.

Pamata instalācija

Ja izmantojat Composer, varat izpildīt šo komandu:

composer require flightphp/core

Tas jūsu sistēmā ievietos tikai Flight kodola failus. Jums būs jādefinē projekta struktūra, izkārtojums, atkarības, konfigurācijas, automātiskā ielāde utt. Šī metode nodrošina, ka netiek instalētas citas atkarības, izņemot Flight.

Varat arī lejupielādēt failus tieši un izvilkt tos savā tīmekļa direktorijā.

Pamata instalācija ir lieliski piemērota mācībām, mikro API un kopēšanas-ielīmēšanas eksperimentiem. Lai iegūtu pilnu lietotnes izkārtojumu, kam cilvēki un AI kodēšanas rīki var sekot vienādi, izmantojiet zemāk ieteikto skeletu.

Ieteicamā instalācija

Ļoti ieteicams sākt ar flightphp/skeleton lietotni jebkuram jaunam projektam. Instalācija ir vienkārša.

composer create-project flightphp/skeleton my-project/
cd my-project/
composer start
# neobligāta parauga DB + ierakstu demonstrācija
php runway migrate

Šis solis izveido projekta struktūru, Composer PSR-4 automātisko ielādi, konfigurāciju un rīkus, piemēram, Tracy, Tracy paplašinājumus un Runway. Tas ietver arī saknes AGENTS.md failu (un ierobežotas kopijas zem app/), lai AI asistenti dalītos ar jums vienā izkārtojumā — skatiet AI un izstrādātāja pieredze.

Ko sniedz skeleton lietotne

project-root/
├── AGENTS.md              # AI / aģentu patiesības avots
├── SECURITY.md            # Drošības gaidības
├── .env.example           # Noslēpumi / izvietošanas pārklājumi (kopēts uz .env)
├── public/index.php       # Tikai tīmekļa ieejas punkts
├── app/
│   ├── config/            # bootstrap, maršruti, pakalpojumi, config_sample.php
│   ├── Controller/        # App\Controller\*  (PascalCase mape!)
│   ├── Middleware/        # App\Middleware\*
│   ├── Model/             # App\Model\* (ActiveRecord)
│   ├── Utils/             # Config, Env, DatabaseFactory
│   ├── commands/          # Runway CLI komandas
│   ├── views/             # Twig veidnes (*.twig)
│   ├── cache/
│   └── log/
├── migrations/            # SQL migrācijas (.sql / .mysql.sql)
└── tests/                 # PHPUnit

Nosaukumvietas seko mapes reģistram. Composer kartē "App\\": "app/", tāpēc:

Ceļš uz diska Nosaukumvieta
app/Controller/HomeController.php App\Controller\HomeController
app/Middleware/… App\Middleware\…
app/Model/… App\Model\…
app/Utils/… App\Utils\…

Operētājsistēmā Linux app/controller/ nav tas pats, kas app/Controller/. Automātiskā ielāde ir reģistrjutīga — atbilst skeleton PascalCase mapēm. Detalizēti: Automātiskā ielāde.

Noklusējuma kopums (jauni projekti): Twig skati, SimplePdo + ActiveRecord, Dice ar Engine ievadīšanu (vēlams izvairīties no Flight:: lietotnes klasēs), neobligāta SQLite pēc php runway migrate.

create-project parasti kopē app/config/config_sample.phpconfig.php un .env.example.env, ja tādi ir. Maršruti atrodas app/config/routes.php; pakalpojumi un DI atrodas app/config/services.php.

Dokumentācija ↔ skeleton: Šie dokumenti māca Flight API (bieži ar īsiem Flight:: piemēriem). Skeleton nosaka lietotnes struktūru. Pievienojot kodu zem app/, sekojiet skeleton kokam; izmantojiet dokumentus metožu nosaukumiem, opcijām un spraudņiem.

Konfigurējiet savu tīmekļa serveri

Iebūvētais PHP izstrādes serveris

Šis ir visvienkāršākais veids, kā sākt darbu. Varat izmantot iebūvēto serveri, lai palaistu savu lietotni, un pat izmantot SQLite kā datubāzi (ja vien sqlite3 ir instalēts jūsu sistēmā), un nekas daudz nav nepieciešams! Vienkārši izpildiet šo komandu pēc PHP instalēšanas:

php -S localhost:8000
# vai ar skeleton lietotni
composer start

Pēc tam atveriet pārlūkprogrammu un dodieties uz http://localhost:8000.

Ja vēlaties, lai jūsu projekta dokumentu sakne būtu cita direktorija (piem., jūsu projekts ir ~/myproject, bet dokumentu sakne ir ~/myproject/public/), varat izpildīt šo komandu, kad esat ~/myproject direktorijā:

php -S localhost:8000 -t public/
# ar skeleton lietotni tas jau ir konfigurēts
composer start

Pēc tam atveriet pārlūkprogrammu un dodieties uz http://localhost:8000.

Apache

Pārliecinieties, ka Apache ir instalēts jūsu sistēmā. Ja nav, meklējiet internetā, kā instalēt Apache savā sistēmā.

Apache gadījumā rediģējiet savu .htaccess failu ar šādu saturu:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Piezīme: Ja jums jāizmanto Flight apakšdirektorijā, pievienojiet rindu RewriteBase /subdir/ tūlīt pēc RewriteEngine On.

Piezīme: Ja vēlaties aizsargāt visus servera failus, piemēram, db vai env failu. Ievietojiet to savā .htaccess failā:

RewriteEngine On
RewriteRule ^(.*)$ index.php

Nginx

Pārliecinieties, ka Nginx ir instalēts jūsu sistēmā. Ja nav, meklējiet internetā, kā instalēt Nginx savā sistēmā.

Nginx gadījumā pievienojiet savai servera deklarācijai šādu konfigurāciju:

server {
  location / {
    try_files $uri $uri/ /index.php;
  }
}

Izveidojiet savu index.php failu

Ja veicat pamata instalāciju, jums būs nepieciešams kods, lai sāktu darbu.

<?php

// Ja izmantojat Composer, iekļaujiet autoloader.
require 'vendor/autoload.php';
// ja neizmantojat Composer, ielādējiet ietvaru tieši
// require 'flight/Flight.php';

// Pēc tam definējiet maršrutu un piešķiriet funkciju pieprasījuma apstrādei.
Flight::route('/', function () {
  echo 'hello world!';
});

// Visbeidzot, palaidiet ietvaru.
Flight::start();

Skeleton lietotnē publiskais ieejas punkts tikai palaiž lietotni. Maršruti tiek reģistrēti app/config/routes.php (parasti [App\Controller\…::class, 'method'], lai Dice varētu ievadīt atkarības). Pakalpojumi, Twig, SimplePdo un konteiners ir savienoti app/config/services.php. Šī struktūra ir apzināta, lai AI rīki un cilvēki katru reizi rediģētu tās pašas vietas.

PHP instalēšana

Ja jūsu sistēmā jau ir instalēts php, izlaidiet šīs instrukcijas un pārejiet uz lejupielādes sadaļu

macOS

PHP instalēšana, izmantojot Homebrew

  1. Instalējiet Homebrew (ja vēl nav instalēts):

    • Atveriet termināli un izpildiet:
      /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  2. Instalējiet PHP:

    • Instalējiet jaunāko versiju:
      brew install php
    • Lai instalētu konkrētu versiju, piemēram, PHP 8.1:
      brew tap shivammathur/php
      brew install shivammathur/php/php@8.1
  3. Pārslēgties starp PHP versijām:

    • Atsaistiet pašreizējo versiju un saistiet vēlamo versiju:
      brew unlink php
      brew link --overwrite --force php@8.1
    • Pārbaudiet instalēto versiju:
      php -v

Windows 10/11

PHP manuāla instalēšana

  1. Lejupielādējiet PHP:

    • Apmeklējiet PHP for Windows un lejupielādējiet jaunāko vai konkrētu versiju (piem., 7.4, 8.0) kā ne-thread-safe zip failu.
  2. Izvelciet PHP:

    • Izvelciet lejupielādēto zip failu uz C:\php.
  3. Pievienojiet PHP sistēmas PATH:

    • Dodieties uz Sistēmas rekvizīti > Vides mainīgie.
    • Sadaļā Sistēmas mainīgie atrodiet Path un noklikšķiniet Rediģēt.
    • Pievienojiet ceļu C:\php (vai kur vien izvilkāt PHP).
    • Noklikšķiniet OK, lai aizvērtu visus logus.
  4. Konfigurējiet PHP:

    • Kopējiet php.ini-development uz php.ini.
    • Rediģējiet php.ini, lai konfigurētu PHP pēc vajadzības (piem., iestatot extension_dir, iespējojot paplašinājumus).
  5. Pārbaudiet PHP instalāciju:

    • Atveriet komandu uzvedni un izpildiet:
      php -v

Vairāku PHP versiju instalēšana

  1. Atkārtojiet iepriekš minētās darbības katrai versijai, ievietojot katru atsevišķā direktorijā (piem., C:\php7, C:\php8).

  2. Pārslēdzieties starp versijām, pielāgojot sistēmas PATH mainīgo, lai tas norādītu uz vēlamo versijas direktoriju.

Ubuntu (20.04, 22.04 utt.)

PHP instalēšana, izmantojot apt

  1. Atjauniniet pakotņu sarakstus:

    • Atveriet termināli un izpildiet:
      sudo apt update
  2. Instalējiet PHP:

    • Instalējiet jaunāko PHP versiju:
      sudo apt install php
    • Lai instalētu konkrētu versiju, piemēram, PHP 8.1:
      sudo apt install php8.1
  3. Instalējiet papildu moduļus (neobligāti):

    • Piemēram, lai instalētu MySQL atbalstu:
      sudo apt install php8.1-mysql
  4. Pārslēgties starp PHP versijām:

    • Izmantojiet update-alternatives:
      sudo update-alternatives --set php /usr/bin/php8.1
  5. Pārbaudiet instalēto versiju:

    • Izpildiet:
      php -v

Rocky Linux

PHP instalēšana, izmantojot yum/dnf

  1. Iespējojiet EPEL krātuvi:

    • Atveriet termināli un izpildiet:
      sudo dnf install epel-release
  2. Instalējiet Remi krātuvi:

    • Izpildiet:
      sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
      sudo dnf module reset php
  3. Instalējiet PHP:

    • Lai instalētu noklusējuma versiju:
      sudo dnf install php
    • Lai instalētu konkrētu versiju, piemēram, PHP 7.4:
      sudo dnf module install php:remi-7.4
  4. Pārslēgties starp PHP versijām:

    • Izmantojiet dnf moduļa komandu:
      sudo dnf module reset php
      sudo dnf module enable php:remi-8.0
      sudo dnf install php
  5. Pārbaudiet instalēto versiju:

    • Izpildiet:
      php -v

Vispārīgas piezīmes

Guides

Ceļveži

Flight PHP ir izstrādāts, lai būtu vienkāršs, bet spēcīgs, un mūsu ceļveži palīdzēs jums izveidot reālas pasaules aplikācijas solis pa solim. Šie praktiskie pamācību materiāli vada jūs cauri pilnīgiem projektiem, lai demonstrētu, kā Flight var tikt izmantots efektīvi.

Oficiālie ceļveži

Būvējot emu blogu

Uzziniet, kā izveidot funkcionējošu bloga aplikāciju ar Flight PHP. Šis ceļvedis vada jūs cauri:

Šī pamācība ir ideāla iesācējiem, kuri vēlas redzēt, kā visi elementi sader kopā reālā aplikācijā.

Vienības testēšana un SOLID principi

Šis ceļvedis aptver vienības testēšanas pamatus Flight PHP aplikācijās. Tajā ietilpst:

Neoficiālie ceļveži

Kaut arī šie ceļveži netiek oficiāli uzturēti Flight komandas, tie ir vērtīgi resursi, kurus izveidojusi kopiena. Tie aptver dažādas tēmas un lietošanas gadījumus, sniedzot papildu ieskatus Flight PHP izmantošanā.

Creating a RESTful API with Flight Framework

Šis ceļvedis vada jūs cauri RESTful API izveidošanai, izmantojot Flight PHP framework. Tajā tiek apskatīti API iestatīšanas pamati, maršrutu definēšana un JSON atbilžu atgriešana.

Building a Simple Blog

Šis ceļvedis vada jūs cauri vienkārša bloga izveidošanai, izmantojot Flight PHP framework. Tajā faktiski ir 2 daļas: viena, lai aptvertu pamatus, un otra, lai apskatītu vairāk uzlabotus tēmus un uzlabojumus ražošanas gatavam blogam.

Building a Pokémon API in PHP: A Beginner's Guide

Šis aizraujošais ceļvedis vada jūs cauri vienkārša Pokémon API izveidošanai, izmantojot Flight PHP. Tajā tiek apskatīti API iestatīšanas pamati, maršrutu definēšana un JSON atbilžu atgriešana.

Iesaistīšanās

Vai jums ir ideja par ceļvedi? Atrasts kļūda? Mēs priecājamies par ieguldījumiem! Mūsu ceļveži tiek uzturēti FlightPHP dokumentācijas repozitorijā.

Ja jūs esat izveidojis kaut ko interesantu ar Flight un vēlaties to dalīties kā ceļvedi, lūdzu, iesniedziet pull request. Daloties ar savām zināšanām, jūs palīdzat Flight kopienai augt.

Meklējat API dokumentāciju?

Ja jūs meklējat specifisku informāciju par Flight galvenajām funkcijām un metodēm, apskatiet Learn sadaļu mūsu dokumentācijā.