Tappava pääsy php. Poikkeusten oikea käyttö PHP:ssä

Mikromuodot

Nämä ovat tunnisteita, joita käytetään kuvaamaan tietyntyyppistä tietoa verkkosivulla. Esimerkiksi arvostelu, tapahtuma, tuote, yritys tai henkilö.

Kaikilla tiedoilla on omat ominaisuutensa. Esimerkiksi "henkilö" sisältää ominaisuuksia, kuten: nimi, osoite, tehtävänimike, työpaikka ja sähköpostiosoite.

Yleensä mikromuodot määritellään attribuutin avulla luokkaa HTML-tunnisteiden sisällä tai

Esimerkki Wikipediasta

Oletetaan, että siellä on kontti, jossa on jo merkittyjä tietoja henkilöstä:

< div> < div>Vasily Pupkin < div>Sarvet ja sorkat < div> 495 - 564 - 1234 < a href= "//example.com/" >Oma sivusto

Mikromuotoa käyttämällä voit lisätä semanttisen merkityksen tähän koodilohkoon:

< div class = "vcard" > < div class = "fn" >Vasily Pupkin < div class = "org" >Sarvet ja sorkat < div class = "tel" > 495- 564- 1234 < a class = "url" href= "//example.com/" >Oma sivusto

Itse elementtien sisältö ei ole muuttunut; niihin lisättiin vain attribuutteja, jotka osoittavat tarkalleen missä lohkossa tämä tai tuo tieto sijaitsee (nimi, puhelinnumero ja niin edelleen). Koko lohkolla on attribuutti class="vcard", joka on mikromuodon emo hCard. Tämä tarkoittaa, että tämä elementti ja kaikki sen sisäkkäiset elementit muodostavat yhdessä hCard-mikromuodon.

Mikroformaatit WordPress-blogille

Blogeille ja muille vastaaville sivustoille mikromuoto on paras. Tämän me toteutamme.

Teematiedostojen muokkaaminen index.php, single.php, search.php

1. Etsi kontti div Kanssa id "sisältö" ja lisää luokka "hfeed".
Esimerkiksi:

< div id= "content" class = "narrowcolumn hfeed" >

3. Muokkaa postauksen otsikkoa niin, että siitä tulee samalla linkki luokkaan "esityksen otsikko".
Esimerkiksi:

" rel="bookmark" title="Pysyvä linkki " class="entry-title">!}

4. Nyt tulee vaikea osa. Viestin julkaisupäivä on ilmoitettava tässä lomakkeessa.
Esimerkki:

">

5. Arkistossa single.php vaihtaa luokkaa "sisääntulo" päällä "sisääntulon sisältö".
Esimerkiksi.

Toinen pieni joukko turhaa tietoa)

Sanhiro:

Yksinkertainen 15-vuotias (alun perin) poika Achain niemimaalta. Aluksi hän asuu kylässään ja haaveilee tulla soturiksi ja suojella kylää rosvoilta ja kapinallisilta, mutta hänen isänsä työskentelyn kaivoksessa tapahtuneiden räjähdysten ja myöhempien tapahtumien ansiosta hänestä tulee vahingossa johtajan viimeisen tahdon omistaja. lohikäärmeet, Moradel, joka eli tuhat vuotta sitten.
Millainen tämän hahmon pitäisi olla, on pelaajan päätettävissä. On kolme pääpolkua... ystävällisyys, pahuus ja paha. Valitsemalla vastauksia dialogeihin Sanhiro muuttuu suuntaan tai toiseen, ja vaikka tämä ei vaikuta juoniin globaalisti ennen kuin aivan viimeisiä lukuja, se... määrittää, mitä lisätehtäviä voidaan saada pelin aikana ja ketkä jäävät sankarin joukkoon. ryhmä ja kuka lähtee.

Mara:

Sanhiron nuorempi sisko. Vuoden veljeäni nuorempi. Aluksi hän haaveilee metsästäjäksi ryhtyvänsä ja kylänsä ruokaan, mutta pakotettuaan kotimaastaan ​​hän etsii uutta tavoitetta elämälleen yhdessä Sanhiron kanssa. Älykkäämpi ja luetumpi kuin hänen veljensä, hän tietää kaiken kaikesta ja voi aina kertoa jotain olennoista, joihin sankarit tapasivat, tai paikoista, joissa he vierailivat. Hän on melko rauhallinen ja menettää harvoin malttinsa, mutta jos näin tapahtuu, Mara pystyy sulkemaan kaikki ja peittämään välittömästi kaikki kiistat ja konfliktit. Matkan edetessä hän etsii ja lukee harvinaisia ​​kirjoja, joista jokainen lisää hieman hänen ominaisuuksiaan.

Farigorn ja Uni:


Matkailijat ja lapsuuden ystävät. Aluksi he esittelevät itsensä kauppiastytönä ja hänen uskollisena vartijana, joka etsii kapinallisjohtajaa Yoshimo Kawaria toivoen hänen tukeaan ja suojelustaan. Sanhiron pitkän tutustumisen jälkeen kuitenkin käy ilmi, että he

Spoileri SPOILERI:

alkuaineagentit, jotka on lähetetty poistamaan Yoshimoa ja tuhoamaan hänen kapinalliset

Olosuhteiden yhdistelmän seurauksena he luopuvat tehtävästään ja liittyvät Sanhiroon ja Maraan. Vähitellen Sanhiro oppii heistä enemmän ja pelaajahahmosta riippuen hänestä tulee joko Farigornin tai Unin hyvä ystävä, ja jokainen näistä vaihtoehdoista avaa pelaajalle lisätehtäviä.
Farigorn arvostaa valtavasti valtavaa miekkaansa, eikä periaatteessa käytä mitään muuta asetyyppiä. Ainoa tapa pakottaa hänet käyttämään jotain muuta kuin miekkaansa on antaa hänelle vielä viileämpi ja vahvempi miekka.
Uni puolestaan ​​on vähemmän valikoiva aseissaan, mutta riippuen vuoropuhelusta pelaajan kanssa hän avaa täysin erilaisia ​​kykyjä.

Asteria:

Asur, "ensimmäinen sisar" ja asurien salaisen järjestön johtaja, joka piileskelee suuren sodan aikana tuhoutuneen Aratanan kaupungissa. Löytää sankarin saatuaan tietää, että hänestä on tullut Moradelin "viimeisen tahdon" omistaja. Hän pelastaa päähenkilön hengen ja kutsuu hänet mukaansa Arataniin, mutta aluksi tämä epäonnistuu ja muuttaa pois. Myöhemmin, kun sankari joutuu jälleen toivottomaan tilanteeseen, hän tulee hänen avukseen, ja tällä kertaa, ja vihdoin saa luottamusta Sanhiroon.
Hän vihaa elementaaliolentoja... ennen kaikkea hän vihaa Ilman arkonia... miksi... Taistelussa hän käyttää kutsumisen ja muuttamisen taikuutta vahvistaen itseään ja pakottamalla kutsumia olentoja. hän hyökkää vastustajia vastaan ​​itsensä sijaan. Hän vihaa Sanhiroa, joka valitsi hyvän tien... mutta ei lähde juonen syistä.
Romanttinen hahmo.

Ian Becket:

Esferato-rodun edustaja ja entinen valoelementaali. Samaan aikaan parantaja ja energiavampyyri. Lisäksi hänellä on arsenaalissaan monia illuusioihin liittyviä kykyjä ja peruskykyjä nekromantian alalla (hän ​​voi puhua kuolleiden kanssa koskettaen heidän verta tai ruumiita ja rajoitetusti hallita kuolleita ruumiita).
Hän liittyy sankariin löytääkseen mystisen esineen, jonka on luonut hänen rotunsa esi-isän - Soranin Grimoire... ja samalla auttaa Sanhiroa tuomaan mahdollisimman paljon kaaosta elementaalimaailmaan. Melkein aina erittäin rauhallinen ja järkevä. Hän ei pidä itseään hyvänä eikä pahana... pahuudesta hän päihittää kaikki muut Sanhiron toverit. Jos Sanhiro valitsee pahan tien, hän voi ystävystyä hänen kanssaan. Sillä on oma tarinansa, hyvin yksityiskohtainen ja hieman pääjuonteeseen vaikuttava.

Ondi (Andrea):

Spontaani tyttö, joka ei muista kuka hän on. Liittyy pelaajiin epätoivosta, kun he auttavat häntä hänen matkansa aikana. Hän on erittäin ystävällinen ja suloinen, vaikkakin altis raivokohtauksille. Ulkonäöstään huolimatta hän on erittäin vahva taistelussa.
Kuka hän todella on, on edelleen sotilaallinen salaisuus.
Romanttinen hahmo.

Amentar:

Afari-soturi tulee villiheimoista. Hän on erittäin ankara, fanaattinen ja toimii ensisijaisesti omien kunniakäsitysteensä pohjalta. Hän on uskollinen oudolle afar-uskontolleen ja nauraa spontaanin perinteille ja uskolle, mutta voi haastaa hänet pyhään kaksintaisteluun, jos joku uskaltaa sanoa jotain hänen kulttuuristaan ​​ja uskostaan. Sillä on oma tarinansa, jonka se kertoo pelaajalle...

Kesalia Kess:

Oudoin ja salaperäisin henkilö (henkilö?) puolueessa. Hän tulee Sanhiroon yksinkertaisesti lepohetkellä tarinan lukujen välillä ja yhtäkkiä ilmoittaa haluavansa lähteä matkalle hänen ja muiden kanssa. Hän ei kerro, kuka hän on, mutta hän pakottaa silti itsensä yritykseen. Matkan edetessä se muuttuu vieraammaksi ja vieraammaksi... tekee selväksi, että hän ei ymmärrä monia inhimillisiä tunteita, haluaa oppia lisää rakkaudesta ja "kokeilla" sitä... valitsee selkeästi Sanhiron tähän ja kiusaa häntä aktiivisesti kaikesta huolimatta. maailmankatsomuksestaan, mutta näyttää siltä, ​​että hän ymmärtää sanan "rakkaus" omalla tavallaan. Vähitellen paljastaa todellisen persoonallisuutensa ja pelaajan maailmankatsomuksesta riippuen joko poistuu puolueesta tai jää.
Romanttinen hahmo.

Tori Troy:

"Holderia", kuten Sanhiroa, vainoavat alkuaineimperiumin viranomaiset. Omistaa lohikäärmeistä koostuvan esineen, jota hän ei voi antaa kenellekään ennen kuin hänen elämänsä päättyy. Hän käyttää tätä artefaktia ainoana käytettävissään olevana aseena, minkä ansiosta hänestä tulee erittäin heikko pelin loppuun mennessä. Hänestä voi kuitenkin tulla hyvä tukisoturi kykyjensä ansiosta, jotka kehittyvät eri tavalla riippuen pelaajan maailmankuvasta ja pelaajan dialogista hänen kanssaan.

Kirjoitan siitä mielelläni "Tämä artikkeli on tarkoitettu aloittelijoille", mutta se ei ole totta. Useimmat PHP-kehittäjät, joilla on 3, 5 tai jopa 7 vuoden kokemus, eivät todellakaan ymmärrä, miten poikkeuksia käytetään oikein. Ei, he ovat hyvin tietoisia olemassaolostaan, että niitä voidaan luoda, käsitellä jne., mutta he eivät ymmärrä niiden mukavuutta, logiikkaa eivätkä pidä niitä täysin normaalina kehityksen elementtinä.

Tämä artikkeli ei sisällä suorituksia koskevaa käsikirjaa - se kaikki on kuvattu täydellisesti php-dokumentaatiossa. Tässä puhun poikkeuksien käytön eduista ja siitä, missä niitä todella pitäisi käyttää. Kaikki esimerkit koskevat Yiiä, mutta tämä ei ole erityisen tärkeää.

Miksi emme osaa käyttää poikkeuksia:

Rakastan PHP:tä. Tämä on kaunis kieli, vaikka sitä kritisoidaan kuinka paljon. Mutta uusille kehittäjille se sisältää tietyn vaaran: se on liian anteeksiantavaa.

PHP on liian rakastava äiti. Ja ilman tiukkaa isää (esimerkiksi Java) tai itsekuria, kehittäjästä kasvaa itsekäs henkilö, joka ei välitä kaikista säännöistä, standardeista ja parhaista käytännöistä. Ja näyttää siltä, ​​että on aika ottaa E_NOTICE käyttöön, mutta hän luottaa silti äitiinsä. Joka muuten vanhenee - hän tarvitsee jo E_STRICT ja E_DEPRICATED, mutta poikani roikkuu edelleen hänen kaulassaan.

Se, onko PHP syyllinen, on keskustelun aihe, mutta tosiasia, että PHP ei alusta alkaen opeta meille poikkeuksia, on tosiasia: sen vakiotoiminnot eivät luo poikkeuksia. Ne joko palauttavat virheen vihjaten, että jotain on vialla, tai kirjoittavat jonnekin virhekoodin, jota et aina ajattele tarkistavan. Tai he menevät toiseen ääripäähän - Fatal Error.

Ja vaikka aloitteleva kehittäjämme yrittää kirjoittaa ensimmäistä redneck-cms:ään, hän ei koskaan kohtaa suoritusmekanismia. Sen sijaan se keksii useita tapoja käsitellä virheitä. Luulen, että kaikki ymmärtävät, mitä tarkoitan - nämä menetelmät palauttavat eri tyyppejä (esimerkiksi objektin onnistumisesta ja merkkijonoa, jossa virhe on epäonnistunut), tai tallentaa virheen johonkin muuttujaan/ominaisuuteen, ja aina - joukon tarkistuksia välitysvirhe puhelupinossa.

Sitten hän alkaa käyttää kolmannen osapuolen kirjastoja: hän kokeilee esimerkiksi Yii:tä ja kohtaa ensimmäistä kertaa poikkeuksia. Ja sitten...

Ja sitten ei tapahdu mitään. Ei yhtään mitään. Hän on jo kehittänyt virheenkäsittelymenetelmiä, joita on kehitetty kuukausien/vuosien aikana – hän jatkaa niiden käyttöä. Jonkun (kolmannen osapuolen kirjasto) kutsuma poikkeus katsotaan tietyntyyppiseksi kohtalokkaaksi virheeksi. Kyllä, se on paljon yksityiskohtaisempi, kyllä ​​se on kirjattu yksityiskohtaisesti, kyllä ​​Yii näyttää kauniin sivun, mutta ei sen enempää.

Sitten hän oppii saamaan ne kiinni ja käsittelemään niitä. Ja tähän hänen tuttavuutensa poikkeuksin päättyy. Loppujen lopuksi hänen täytyy työskennellä, ei opiskella: hänellä on jo tarpeeksi tietoa (sarkasmi)!

Mutta pahinta on, että poikkeuksiin kehittyy asenne huonona, ei-toivottavana, vaarallisena, jota ei pitäisi olla ja jota tulee kaikin keinoin välttää. Tämä ei todellakaan ole oikea lähestymistapa.

Poikkeusten edut

Itse asiassa poikkeuksien käyttö on erittäin lakoninen ja kätevä ratkaisu virheiden luomiseen ja käsittelyyn. Tässä ovat tärkeimmät edut:

Kontekstin logiikka

Ensinnäkin haluan osoittaa, että poikkeus ei aina ole vain virhe (kuten kehittäjät sen yleensä ymmärtävät). Joskus se voi olla osa logiikkaa.

Meillä on esimerkiksi toiminto JSON-objektin lukemiseksi tiedostosta:

/** * Lukee objektin JSON-tiedostosta * @param-merkkijono $tiedosto * @heittää FileNotFoundException-tiedostoa ei löydy * @heittää JsonParseExceptionin virheellisen json-muodon * @return sekoitettu */ julkinen toiminto readJsonFile($file) (... )
Oletetaan, että yritämme lukea joitain aiemmin ladattuja tietoja. Tällaisella toiminnolla FileNotFoundException-poikkeus ei ole virhe ja se on varsin hyväksyttävä: ehkä emme ole koskaan ladanneet tietoja, joten tiedostoa ei ole olemassa. Mutta JsonParseException on jo merkki virheestä, koska tiedot on ladattu, käsitelty, tallennettu tiedostoon, mutta jostain syystä niitä ei tallennettu oikein.

Se on täysin eri asia, kun yritämme lukea tiedostoa, jonka pitäisi aina olla siellä: tällaisella toiminnolla FileNotFoundException on myös virhesignaali.

Näin ollen poikkeukset antavat meille mahdollisuuden määritellä niiden käsittelyn logiikka kontekstista riippuen, mikä on erittäin kätevää.

Yksinkertaistaa sovelluslogiikkaa ja arkkitehtuuria

Kokeile poikkeuksia, niin näet kuinka koodistasi tulee tiiviimpi ja ymmärrettävämpi. Kaikki kainalosauvamekanismit häviävät, ehkä joukko sisäkkäisiä ifejä poistetaan, erilaiset mekanismit virheiden siirtämiseksi puhelupinossa, logiikasta tulee yksinkertaisempaa ja yksinkertaisempaa.

Paikat, joissa poikkeuksia kutsutaan, auttavat kollegaasi ymmärtämään paremmin liiketoimintalogiikkaa ja aihealuetta, sillä koodiisi syventymällä hän näkee heti, mikä on hyväksyttävää ja mikä ei.

Ja jos ajattelemme mitä tahansa omavaraista koodinpalaa, esimerkiksi komponenttia, sen heittämä poikkeusluettelo tekee vielä yhden tärkeän asian: se täydentää tämän komponentin käyttöliittymää.

Kolmannen osapuolen komponentteja käytettäessä olemme tottuneet kiinnittämään huomiota vain positiiviseen puoleen - siihen, mitä se voi tehdä. Samaan aikaan emme yleensä ajattele poikkeuksia, joita se voi luoda käytön aikana. Poikkeusluettelo varoittaa heti, missä, milloin ja mitä ongelmia voi syntyä. Ja ennakolta varoitettu tarkoittaa esiaseistautunutta.

Tässä on esimerkki informatiivisesta käyttöliittymästä, jota on täydennetty tiedolla poikkeuksista:

Interface KladrService ( /** * Määrittää KLADR-koodin osoitteessa * @param Osoite $osoite * @return merkkijonokoodi osoitteelle * @throws AddressNotFoundException osoitetta ei löytynyt osoitetietokannasta * @throws UnresoledAddressException osoite löytyi, mutta sille ei ole Kladr-koodia * / julkinen toiminto solveCode(Osoite $osoite /** * Määrittää osoitteen KLADR-koodilla * @param string $code * @return Address * @throws CodeNotFoundException KLADR-koodia ei löydy); */ julkinen toiminto solveAddress($code);
On syytä mainita, että suoritusluokkia kehitettäessä tulee noudattaa informatiivisen käyttöliittymän periaatetta. Karkeasti sanottuna, ota huomioon niiden looginen merkitys, älä fyysistä. Jos esimerkiksi tallennamme osoitteita tiedostoihin, osoitetiedoston puuttuminen aiheuttaa FileNotFoundExceptionin. Meidän on siepattava se ja annettava merkityksellisempi AddressNotFoundException .

Objektien käyttäminen

Tietyn luokan käyttäminen virheenä on erittäin kätevä ratkaisu. Ensinnäkin luokkaa ei voi sekoittaa: katso kaksi tapaa käsitellä virhettä:

If(Yii::app()->kladr->getLastError() == ‘Osotetta ei löydy’)(…. )
yritä( ... ) catch(AddressNotFoundException $e)( ... )
Ensimmäisessä vaihtoehdossa yksinkertainen kirjoitusvirhe rikkoo koko logiikkasi, mutta toisessa on yksinkertaisesti mahdotonta tehdä virhettä.

Toinen etu on, että suoritusluokka kapseloi kaikki sen käsittelyyn tarvittavat tiedot. Esimerkiksi AddressNotFoundException voi näyttää tältä:

/** * Osoitetta ei löydy osoitetietokannasta */ luokka AddressNotFoundException laajentaa poikkeusta ( /** * Osoitetta ei löydy * @var Osoite */ yksityinen $osoite; /** * @param Osoite $osoite */ public function __construct( Osoite $osoite) ( Poikkeus::__construct("Osotetta ei löydy ".$osoite->oneLine); $this->address = $osoite; ) /** * @return Osoite */ julkinen toiminto getAddress() ( return $ tämä ->osoite ) )
Kuten näet, poikkeus sisältää osoitteen, jota ei löydy. Käsittelijä voi vastaanottaa sen ja toteuttaa jonkin oman logiikkansa sen perusteella.

Kolmas etu ovat itse asiassa kaikki OOP:n edut. Vaikka poikkeukset ovat pääsääntöisesti yksinkertaisia ​​objekteja, niin OOP:n ominaisuuksia käytetään vähän, mutta niitä käytetään.

Esimerkiksi minulla on noin 70 suoritusluokkaa sovelluksessani. Niitä on useita - peruslajeja - yksi luokka per moduuli. Kaikki muut peritään moduulinsa perusluokista. Tämä tehtiin lokianalyysin helpottamiseksi.

Käytän myös useita INTERFACE-MERKKEJÄ:

  • Kirjautumaton käyttöliittymä: Oletuksena kirjaan kaikki käsittelemättömät virheet. Tällä käyttöliittymällä merkitsen poikkeuksia, joita ei tarvitse kirjata ollenkaan.
  • Esikirjattu käyttöliittymä: Tällä käyttöliittymällä merkitsen poikkeuksia, jotka on joka tapauksessa kirjattava: sillä ei ole väliä, käsitelläänkö ne vai ei.
  • OutableInterface: Tämä käyttöliittymä merkitsee poikkeuksia, joiden teksti voidaan näyttää käyttäjälle: jokaista poikkeusta ei voida näyttää käyttäjälle. Voit esimerkiksi näyttää poikkeuksen tekstin kanssa "Sivua ei löydy"- Tämä on hyvä. Mutta et voi näyttää poikkeusta tekstillä "Ei voitu muodostaa yhteyttä Mysqliin rootin ja salasanan 123 avulla". OutableInterface merkitsee poikkeuksia, jotka voidaan tulostaa (minulla on niistä vähemmistö). Muussa tilanteessa jotain sellaista “Palvelu ei saatavilla”.

Oletuskäsittelijä, kirjaus

Oletuskäsittelijä on erittäin hyödyllinen asia. Kuka ei tiedä: se suoritetaan, kun poikkeusta ei voitu käsitellä millään try catch -lohkolla.

Tämän käsittelijän avulla voimme suorittaa erilaisia ​​toimintoja ennen skriptin pysäyttämistä. Tärkein tehtävä on:

Palautusmuutokset: Koska toimintoa ei ole suoritettu kokonaan, kaikki tehdyt muutokset on peruutettava. Muutoin vahingoitamme tietoja. Voit esimerkiksi avata tapahtuman CController::beforeAction() -ohjelmassa, sitoutua CController::afterAction() -sovelluksessa ja virheen sattuessa tehdä palautuksen oletuskäsittelijässä.

Tämä on melko karkea peruutusmenetelmä, ja palautus ei useinkaan tarkoita vain tapahtumien peruuttamista, ja tiedon siitä, kuinka peruuttaa oikein, pitäisi olla liiketoimintalogiikkakoodissa. Tällaisissa tilanteissa sinun tulee käyttää tätä tekniikkaa:

Julkinen funktio addPosition(Sijainti $sijainti) ( kokeile ( ... suorita toimenpide... ) catch(Poikkeus $e) ( ... palautusmuutoksia... heittää $e; // Heitä sama poikkeus uudelleen ) )
Kävi ilmi, että peruutimme muutokset ja teimme saman poikkeuksen jatkaaksemme sen käsittelyä.

Kirjautuminen: oletuskäsittelijä antaa meille myös mahdollisuuden suorittaa mukautettuja kirjauksia. Esimerkiksi sovelluksessani laitan kaiken tietokantaan ja käytän omaa analyysityökaluani. Töissä käytämme osoitteessa getsentry.com/welcome. Joka tapauksessa oletuskäsittelijän saavuttava poikkeus on todennäköisesti odottamaton poikkeus, ja se on kirjattava lokiin. On huomattava, että poikkeusluokkaan voidaan lisätä erilaisia ​​tietoja, jotka on kirjattava lokiin, jotta virheen syy ymmärretään paremmin.

Mahdottomuus olla huomaamatta ja hämmentää

Toteutuksen valtava etu on sen yksiselitteisyys: on mahdotonta olla huomaamatta eikä sitä voida sekoittaa johonkin.

Ensimmäisestä seuraa, että olemme aina tietoisia tapahtuneesta virheestä. Ja se on hienoa – on aina parempi tietää ongelmasta kuin olla tietämättä.

Toinen etu tulee ilmeiseksi verrattuna mukautettuihin virheenkäsittelymenetelmiin, esimerkiksi kun menetelmä palauttaa nollan, jos vaadittua objektia ei löytynyt, ja falsen virheen sattuessa. Tässä tapauksessa virhe on helppo jättää huomiotta:

$tulos = $this->doAnything(); // null jos vaadittua objektia ei löytynyt ja false virheen sattuessa // Ei huomaa virhettä if($result)( ... ) // Ei huomaa virhettä if($result == null)( ... ) // Ei huomaa virheitä if(empty($result))( ... ) // Ei huomaa virheitä if($result = null)( ... )
Toteutusta ei voi jättää väliin.

Virheellisen toiminnon lopettaminen

Mutta tärkein ja tärkein asia, jonka poikkeus tekee, on lopettaa toiminnan jatkaminen. Operaatio, joka on jo mennyt pieleen. Ja siksi, jonka tulos on arvaamaton.

Kotitekoisten virheenkäsittelymekanismien valtava haittapuoli on tarve tarkistaa itsenäisesti virheen esiintyminen. Esimerkiksi jokaisen toimenpiteen jälkeen meidän on kirjoitettava jotain tällaista:

$this->doOperation(); if($this->getLastError() !== null) ( echo $this->getLastError(); kuole; )
Tämä vaatii kehittäjiltä tiettyä kurinalaisuutta. Mutta kaikki eivät ole kurinalaisia. Kaikki eivät edes tiedä, että objektillasi on getLastError()-metodi. Kaikki eivät ymmärrä, miksi on niin tärkeää tarkistaa, että kaikki menee niin kuin pitää, ja jos ei, peruuta muutokset ja lopeta suoritus.

Seurauksena on, että tarkastuksia ei tehdä, ja toimenpiteen suorittaminen johtaa täysin odottamattomiin tuloksiin: yhden käyttäjän sijaan kaikki poistetaan, rahaa lähetetään väärälle henkilölle, valtionduumassa äänestäminen tuottaa väärän tuloksen - olen nähnyt tämän kymmeniä kertoja.

Poikkeus suojaa meitä tällaisilta ongelmilta: se joko etsii sopivaa käsittelijää (sen olemassaolo tarkoittaa, että kehittäjä on ennakoinut tämän tilanteen ja kaikki on hyvin), tai se saavuttaa oletuskäsittelijän, joka voi peruuttaa kaikki muutokset, kirjata virheen, ja anna käyttäjälle asianmukainen varoitus.

Milloin kutsua poikkeuksia:

Olemme ilmeisesti selvittäneet edut. Toivon, että pystyin osoittamaan, että poikkeukset ovat erittäin kätevä mekanismi.

Herää kysymys: missä tilanteissa pitäisi kutsua poikkeusta?

Lyhyesti sanottuna - aina! Yksityiskohtaisesti: aina, kun olet varma, että leikkaus tulisi suorittaa normaalisti, mutta jokin meni pieleen, etkä tiedä mitä tehdä.

Katsotaanpa yksinkertaisinta toimintoa tietueen lisäämiseksi:

/** * Luo postauksen */ julkinen funktio actionCreate() ( $post = \Yii::app()->request->loadModel(new Post()); if($post->tave()) ( $ this ->outSuccess($post ) else ( $this->outErrors($post); ) )
Kun syötämme virheellisiä postitietoja, poikkeusta ei kutsuta. Ja tämä on täysin yhdenmukainen kaavan kanssa:

  • Tässä vaiheessa emme ole varmoja toimenpiteen onnistumisesta, koska käyttäjän syöttämiin tietoihin ei voi luottaa.
  • Tiedämme mitä tehdä asialle. Tiedämme, että virheellisten tietojen tapauksessa meidän on näytettävä käyttäjälle luettelo virheistä. Tässä on huomattava, että tieto "mitä tehdä" kuuluu nykyiseen menetelmään.
Siksi tässä tapauksessa ei ole tarvetta käyttää poikkeuksia. Mutta katsotaanpa toista esimerkkiä: Tilaussivulla on painike, joka peruuttaa tilauksen. Tilauksen peruutuskoodi näyttää tältä:

/** * Peruuttaa tilauksen.
* Peruutus tapahtuu muuttamalla tilaksi STATUS_CANCEL.

* @heittää \Exceptionin */ public function cancel() ( // Tarkista onko STATUS_CANCEL sallittu if(!$this->isAllowedStatus(self::STATUS_CANCEL)) ( heittää new \Exception("Peruutustila ei sallittu") ; ) // Tilan muuttaminen $this->status = self::STATUS_CANCEL $isSaved = $this->save( // Tarkistetaan, että kaikki on tallennettu onnistuneesti ja että tila säilyi STATUS_CANCEL if(!$isSaved); |. $this->status !== self::STATUS_CANCEL) ( heittää uusi \Exception("Huono logiikka peruutuksessa"); ) )

Itse peruutuspainike näkyy vain, kun tilaus voidaan peruuttaa. Tällä tavalla, kun tätä menetelmää kutsutaan, olen varma, että toiminnon on onnistuttava (muuten painike ei tule näkyviin, eikä käyttäjä voisi kutsua tätä menetelmää napsauttamalla sitä).

Ensimmäinen askel on esivalidointi - tarkistamme, voimmeko todella suorittaa toimenpiteen. Teoriassa kaiken pitäisi mennä hyvin, mutta jos isAllowedStatus palauttaa false, se tarkoittaa, että jotain meni pieleen. Lisäksi nykyisellä menetelmällä meillä ei ole minkäänlaista aavistustakaan siitä, miten tämä tilanne pitäisi toimia. On selvää, että meidän on kirjattava virhe, näytettävä se käyttäjälle jne... Mutta tämän menetelmän yhteydessä emme tiedä mitä tehdä sen kanssa. Siksi lopetimme toteutuksen.

Sitten tulee jälkivalidointi - tarkistamme, onko kaikki todella säilynyt ja onko tila todella muuttunut. Ensi silmäyksellä tämä saattaa tuntua merkityksettömältä, mutta: tilausta ei ehkä ole tallennettu (esimerkiksi se ei läpäissyt vahvistusta), ja tila olisi hyvin voitu muuttaa (esimerkiksi joku sotkee ​​CActiveRecord::beforeSave) . Siksi nämä toimet ovat välttämättömiä, ja jälleen kerran, jos jokin menee pieleen, teemme poikkeuksen, koska tällä menetelmällä emme osaa käsitellä näitä virheitä.

Tässä sinun tulee kiinnittää huomiota menetelmän vastuisiin. Esimerkiksi CActiveRecord::find() ei anna poikkeusta, ja tämä on loogista - sen "tietotaso" ei sisällä tietoa siitä, onko tuloksen puuttuminen virhe. Toinen asia on esimerkiksi KladrService::resolveAddress()-metodi, jonka on joka tapauksessa palautettava osoiteobjekti (muuten joko koodi on virheellinen tai tietokanta ei ole ajan tasalla). Tässä tapauksessa sinun on lopetettava suoritus, koska tuloksen puuttuminen on virhe.

Yleensä kuvattu kaava määrittää ihanteellisesti paikat, joissa on tarpeen heittää poikkeuksia. Haluaisin kuitenkin erityisesti korostaa kahta poikkeusluokkaa, jotka on tehtävä mahdollisimman paljon:

Tekniset poikkeukset

Nämä ovat poikkeuksia, jotka eivät millään tavalla liity aihealueeseen ja ovat välttämättömiä puhtaasti teknisesti virheellisen logiikan suorittamisen estämiseksi.

Tässä muutamia esimerkkejä:

// Useissa if-lauseissa if($ehto1) ( $this->do1(); ) elseif($condition2) ( $this->do2(); ) ... else ( // Kun jokin if-lohkoista käynnistyy , mutta se ei toiminut - heittäkää poikkeus ja heittäkää uusi BadLogicException)
// Sama asia swith switch($c) ( case "one": return 1; case "two" return 2; ... oletus: // Kun jonkin tapauslohkoista pitäisi toimia, mutta se ei toimi, heitä poikkeus uusi BadLogicException )
// Tallennettaessa aiheeseen liittyviä malleja if($model1->isNewRecord) ( // Jos ensimmäistä mallia ei ole tallennettu, sillä ei ole tunnusta, rivi $malli2->parent_id = $malli1->id // tekee tiedot ovat rikki, joten sinun on tarkistettava heittää uusi BadLogicException ) $malli2->parent_id = $malli1->id;
// Pelkkää tallennusta - hyvin usein kehittäjät käyttävät savea eivätkä tarkista tulosta if(!$model->save()) ( heittää uusi BadLogicException; )
/** * Laajuus käyttäjätunnuksen mukaan * @param int $userId * @return $this */ julkinen funktio käyttäjätunnuksella($userId) ( if(!$userId) ( // Jos et kutsu tätä poikkeusta, niin jos userId on tyhjä, laajuutta ei kerätä ollenkaan ei sovelleta throw new InvalidArgumentException ) $this->dbCriteria->compare("userId", $userId);
Tekniset poikkeukset auttavat estämään tai havaitsemaan, IMHO, useimmat viat missä tahansa projektissa. Ja niiden käytön kiistaton etu on, että aihealuetta ei tarvitse ymmärtää: ainoa vaadittava asia on kehittäjän kurinalaisuus. Kehotan sinua olemaan laiska ja toteuttamaan tällaisia ​​tarkastuksia kaikkialla.

Väitteet poikkeukset

Väitepoikkeuksia (DDD:hen perustuvia) kutsutaan, kun havaitsemme, että jotakin liiketoimintalogiikkaa rikotaan. Tietenkin ne liittyvät läheisesti domain-tietoon.

Ne heitetään, kun tarkistamme lauseen ja näemme, että tarkistuksen tulos ei vastaa odotettua.

Esimerkiksi on olemassa tapa lisätä nimike tilaukseen:

/** * Lisää sijoituksen tilaukseen * @param Position $position * @heitot \Poikkeus */ julkinen funktio addPosition(Sijainti $sijainti) ( $this->positions = $position; ... laske paikkojen hinta uudelleen, toimituskulut, alennukset, kokonaiskustannukset... // tarkista laskennan oikeellisuus if($this->totalCost != $this->positionsCost + $this->deliveryCost - $this->totalDiscounts) ( heittää uusi \Exception( "Kustannusten uudelleenlaskentavirhe"); ) ...Päivitetään toimitusparametreja... //tarkista, voimmeko toimittaa tilauksen uudella tuotteella if(!Yii::app()->deliveryService->canDelivery($this)) ( heittää uusi \Poikkeus("Ei toimitusta uudella sijainnilla") ) … muut toiminnot... )
Aseman lisäysprosessissa tapahtuu paljon erilaisia ​​toimintoja. Ja samaan aikaan erilaisia ​​lausuntoja tarkistetaan määräajoin: että kaikki summat ovat yhtäpitäviä, että tilaus voidaan toimittaa - nämä ovat poikkeuksia lausunnoista.

Täällä voit keskustella tällaisten poikkeusten tarpeesta:
Voit esimerkiksi kirjoittaa testejä menetelmille tilauksen kustannusten uudelleenlaskentaa varten, ja menetelmän tekstin tarkistaminen ei ole muuta kuin testin kopioimista. Voit tarkistaa tilauksen toimittamisen uudella tuotteella ennen tuotteen lisäämistä (vähintään varoittaaksesi käyttäjää tästä)

Mutta käytäntö osoittaa, että aina ei ole mahdollista kirjoittaa testejä objektin kaikille invarianteille. Ja on mahdotonta suojautua esimerkiksi uudelta kehittäjältä, joka voi pilata mitä tahansa.

Siksi kriittisissä paikoissa tällaisia ​​poikkeuksia tarvitaan ehdottomasti.

Logiikkaa muutetaan poikkeuksien välttämiseksi

Kuten jo sanoin, PHP-kehittäjät pelkäävät poikkeuksia. He pelkäävät ulkonäköään ja uskaltavat jättää heidät yksin.

Ja tässä taistelussa poikkeuksia vastaan ​​monet tekevät virheen: he vetäytyvät alunperin selkeästä, ymmärrettävästä, suoraviivaisesta logiikasta kohti joitakin olettamuksia saadakseen toiminnan jotenkin loppuun.

Tässä on esimerkki: sinun tarvitsee vain näyttää sivu tunnuksella (joten ymmärrät, tämä on oikeaa koodia tunnetusta projektista)

/** * Näyttää sivun tunnuksella * @param int $id */ julkinen toiminto actionView($id = 1) ( $sivu = Sivu::malli()->findByPk($id) ?: Sivu::malli( ) ->find(); $this->render("view", ["sivu" => $sivu] )
Huolimatta yksinkertaisimmasta ja ymmärrettävimmästä tehtävästä, tässä on ehdottoman villi logiikka.
Se ei vain voi näyttää käyttäjälle jotain täysin vialla, vaan se myös peittää virheemme:

  • jos id:tä ei ole määritetty, otetaan id = 1. Ongelmana on, että kun id:tä ei ole määritetty, tämä on jo bugi, koska jossain linkkejämme ei ole muodostettu oikein.
  • Jos sivua ei löydy, se tarkoittaa, että jossain meillä on linkki olemattomalle sivulle. Tämä on myös todennäköisesti bugi.
Tämä käyttäytyminen ei hyödytä käyttäjää tai kehittäjiä. Motivaatio tällaiselle toteutukselle on näyttää ainakin jotain, koska 404-suoritus on huono.

Toinen esimerkki:

/** * Palauttaa kaupungin cladra-koodin * @param sekoitettu $alue * @param sekoitettu $city * @return string */ julkinen funktio getCityKladrCode($alue, $city) ( if($code = ... saa koodin kohteelle kaupunki ..) ( palauta $koodi; ) palauta ... alueen koodin saaminen... )
Myös oikeasta projektista, ja motivaatio on sama: palauttaa ainakin jotain, mutta ei kutsua poikkeusta huolimatta siitä, että menetelmän pitäisi selvästi palauttaa kaupunkikoodi, ei aluetta.

Ja keskivertoprojektissa on valtava määrä tällaisia ​​logiikkamuutoksia. Niin kauan kuin muistat tämän, se näyttää vaarattomalta. Mutta heti kun unohdat tai joku muu kehittäjä tulee mukaan, vika on taattu. Lisäksi se on implisiittinen bugi, kelluva.

Minun mielipiteeni on, että tätä ei voida hyväksyä. Se on vain niin, että kun työskentelet suurilla rahoilla (ja minä työskentelin sen kanssa melko pitkään), tietyt säännöt kehitetään, ja yksi niistä on keskeyttää toiminta, jos epäillään virhettä. Kauppa 10 miljoonalla taalalla: samaa mieltä, on parempi peruuttaa se kuin siirtää rahaa väärälle henkilölle.

Tietenkin käsittelemme yleensä vähemmän riskialttiita toimintoja. Ja esimerkiksi virheen sattuessa vammainen ei voi lähettää pyyntöä rampin asentamisesta sisäänkäynnille. Ja kehittäjä rennosti (häntä ei edes saa sakkoja) laiminlyö nämä perussäännöt, kuten, ajattele vain, mikä pikku juttu. Ongelmana on, että kun hänelle uskotaan jotain erittäin tärkeää, hänen lähestymistapansa ei todennäköisesti muutu. Sillä ongelma ei ole tieto tai riski, vaan kuri ja asenne työhön. Ja käy ilmi, että tällaisten ohjelmoijien jälkeen putket halkeavat talvella, jossain öljyä vuotaa tonneina, jossain ihmisiä kuolee kymmeniä, jossain rahaa varastetaan miljoonia. Ajattele vain, mikä pikkujuttu!

koirat

Jostain syystä ajattelin, että kukaan ei enää käytä koiria. Mutta äskettäin törmäsin kehittäjäryhmään, joka käyttää niitä kaikkialla issetin tarkistamisen sijaan, joten päätin kirjoittaa niistä.

Koiria käytetään issetin sijaan koodin pitämiseksi ytimekkäänä:

@$politiikka->omistaja->osoite->paikkakunta;
vastaan

Isset($käytäntö->omistaja->osoite) ? $käytäntö->omistaja->osoite->paikkakunta: null;
Todellakin, se näyttää paljon lyhyemmältä, ja ensi silmäyksellä tulos on sama. Mutta! On vaarallista unohtaa, että koira on virheilmoitusten huomioimatta jättämisen käyttäjä. Ja @$policy->owner->address->locality palauttaa nollan, ei siksi, että se tarkistaisi objektiketjun olemassaolon, vaan koska se yksinkertaisesti jättää huomioimatta ilmenneen virheen. Ja nämä ovat täysin eri asioita.

Ongelmana on, että sen lisäksi, että Yritetään saada ei-objektivirhe -ominaisuus jätetään huomiotta (joka tekee koiran käytöksestä samanlaisen kuin isset), kaikki muut mahdolliset virheet jätetään huomiotta.

PHP on maaginen kieli! Kaikilla näillä taikamenetelmillä (__get, __set, __call, __callStatic, __invoke jne.) emme aina voi heti ymmärtää, mitä todella tapahtuu.

Katso esimerkiksi riviä $käytäntö->owner->address->locality . Ensi silmäyksellä se näyttää esineiden ketjulta, mutta jos katsot tarkasti, se voi hyvinkin olla tällainen:

  • käytäntö - CActiveRecord-malli
  • omistaja-suhde
  • osoite - getteri, joka esimerkiksi käyttää jotakin kolmannen osapuolen palvelua
  • paikkakunta - attribuutti

Eli yksinkertaisella rivillä $käytäntö->owner->address->locality käynnistämme tuhansien koodirivien suorittamisen. Ja koira ennen tätä riviä piilottaa virheet missä tahansa näistä riveistä.

Siten tällainen ajattelematon koiran käyttö voi aiheuttaa valtavan määrän ongelmia.

Jälkisana

Ohjelmointi on upeaa toimintaa. Minusta se näyttää valtavan LEGO-setin kokoamiselta. Heti alussa sinulla on ohjeet ja siro pieniä yksityiskohtia. Ja niin, otat ohjeet, joiden mukaan keräät ne järjestelmällisesti pieniksi lohkoiksi, sitten yhdistät ne johonkin enemmän, vielä enemmän... Ja saat jännitystä tästä pirun jännittävästä prosessista, saat jännitystä siitä, kuinka loogista ja harkiten kaikki on järjestetty, miten Kaikki nämä osat sopivat yhteen. Ja nyt - edessäsi on jo koko traktori tai kippiauto. Ja se on hämmästyttävää!

Ohjelmoinnissa se on sama, vain ohjeiden roolia on tieto kuvioista, luokkasuunnittelun periaatteista, parhaista ohjelmointikäytännöistä ja arkkitehtuurin rakentamisesta. Ja kun omaksut kaiken tämän ja opit soveltamaan sitä käytäntöön, alat saada työstä jännitystä, kuten LEGOa koottaessa.

Mutta yritä koota rakennussarja ilman ohjeita... Tämä idea näyttää hölynpölyltä. Ohjelmoijat voivat kuitenkin toimia hienosti ilman kaikkea tätä tietoa. Vuosia. Ja se ei näytä heistä hölynpölyltä - he eivät edes ymmärrä tekevänsä jotain väärin. Sen sijaan he valittavat, että heille annettiin liian vähän aikaa.

Ja jos edellisen postauksen jälkisanassa ehdotin ajattelemista, toivoen, että joku muuttaisi koodiaan parempaan suuntaan, niin nyt olen menettänyt tämän toivon. Ilmeisesti ihmiset arvostavat vain sitä kokemusta, josta he maksoivat.

Joten kaikille, jotka lukivat tämän postauksen ja ajattelivat "mitä hölynpölyä", "Tiedän tämän kaiken, mutta olen liian laiska käyttämään sitä" tai "paskari kertoo minulle" - haluan tehdä virheen. Virhe, josta sinua sakotetaan tai erotetaan. Ja sitten saatat muistaa tämän viestin ja ajatella: "ehkä teen todella jotain väärin"?

Tee se valmiiksi mahdollisimman pian. Sillä on parempi tehdä virhe kerran, mutta nähdä valoa, kuin elää koko elämäsi punaniskakooderina. Aamen.

kaikkea hyvää)

Tunnisteet: Lisää tunnisteita