WWW-ohjelmointi ANTTI SAND TIETA12
Namespaces 2
Namespaces 3 No man is an island Haluamme hyödyntää muiden kirjoittamia komponentteja Mutta tähän asti kaikki luokkamme sijaitsevat globaalissa nimiavaruudessa Jos meillä on luokka Request ja haluamme käyttää pakettia, joka myös määrittää luokan Request, seuraa poikkeus Tarvitaan tapa jaotella luokat nimiavaruuksiin Yksinkertaisimmillaan seuraavasti
Suositeltuja käytäntöjä 4 Yksi luokka per tiedosto Yksi nimiavaruus per tiedosto Yksi pakettikohtainen päänimiavaruus (tässä geneerinen App) Nimiavaruuksien olisi hyvä vastata hakemistorakennetta App\Core\Bar = App/Core/Bar.php Mutta tähän ei pakoteta Mutta tästä on myöhemmin hyötyä
Use namespace 5 Voidaan kirjoittaa luokan täydellinen nimiavaruus Jos ollaan nimiavaruudessa App ja halutaan App\Core\Bar, voidaan kirjoittaa vain Core\Bar Tai jos halutaan Vendor\Bar, kirjoitetaan \Vendor\Bar, jolloin haetaan juuresta Tai voidaan käyttää (use) nimiavaruutta Ja voidaan halutessaan antaa alias (as Bar1) TIETA12-2017 - A.S.
Namespace caveats 6 Kielen omat luokat eivät kuulu sovelluksen nimiavaruuteen Esimerkiksi Router palauttaa poikkeuksen (Exception), joka ei kuulu luokan omaan nimiavaruuteen App\Core Jos kutsuja on vain yksi, voidaan kirjoittaa throw new \Exception(.. ); Tai jos kutsuja on useita (kuten QueryBuilderin PDO), voidaan kirjoittaa normaalisti use PDO;
Täydellinen nimi vai use? 7 Jos käytät jotain luokkaa vain muutamassa kohdassa, voit kirjoittaa täydellisen nimen $request = new \App\Core\Request(); Mutta jos komponenttia käytetään toistuvasti samassa luokassa, voidaan tuoda se osaksi luokan nimiavaruuksia Use App\Core\Request; $request = new Request(); Jos sinulla on useampia kuin yksi samanniminen luokka, voit käyttää täydellisiä nimiä, käyttää toista nimeä, tai käyttää molempia, mutta lisätä alias $a = new \App\Core\MyClass(); $b = new \Support\Something\MyClass();
Namespace caveats 8 Aiemmin käytimme Model luokassa get_called_class() funktiota määrittämään taulun nimen Kun siirrymme käyttämään nimiavaruuksia, tuo funktio palauttaa täydellisen nimen (l. Sisältää nimiavaruuden) Eli jos laitamme luokan Task nimiavaruuteen App\Models, funktio palauttaisi App\Models\Task Muokataan siis Model luokan gettablename metodia palauttamaan vain viimeisen kenoviivan jälkeinen osuus
9 PHP-FIG Framework Interop Group - http://www.php-fig.org/psr/
PHP-FIG 10 Ohjelmointikehyksen kehittyivät eristyksissä toisistaan, ne eivät jakaneet toiminnallisuutta, eivätkä noudattaneet samoja käytäntöjä CodeIgniter toteutti toiminnallisuuden X tavalla Y Ja Zend toteutti toiminnallisuuden X tavalla Z Php tek2009 konferenssissa muodostettiin PHP-FIG työryhmä sopimaan yhteisistä käytännöistä Luodaan komponentteja, jotka noudattavat sovittuja käytäntöjä ja koostetaan ohjelmointikehykset näistä komponenteista Kehittäjä voi vaihdella ja yhdistellä käytännön toteutuksia mieleisellään tavalla Komponentit tarjoavat sovitun rajapinnan, mutta itse toteutus vaihtelee
11 PHP-FIG antaa suosituksia rakenteelle PSRs = PHP Standards Recommendations PSR1&2 antavat suosituksia merkkaustavoista PSR0&4 määrittävät nimiavaruuksien käytön tiedostojen automaattiseen lataamiseen Ei enää tarvetta require file.php ;
Composer 12
Composer 13 Composer on paketinhallintatyökalu PHP:lle Voidaan merkata jokin paketti riippuvuudeksi Composer huolehtii sen asentamisesta ja päivittämisestä Vertaa NPM https://getcomposer.org/
Packagist 14 Packagist on koostesivu PHP komponenteista https://packagist.org/ The League of Extraordinaty Packages https://thephpleague.com/ Voit etsiä tarpeeseesi sopivan komponentin ja asentaa sen Composerilla Composer require guzzlehttp/guzzle Composer lisää komponentin composer.json tiedostoon projektin riippuvuudeksi Uusi kehittäjä tulee mukaan projektiin, ajaa composer install ja saa projektin riippuvuudet omalle kehityskoneelleen
Composer.json 15 NPM Packages.json Composer Composer.json Määrittää JSON -muodossa vaaditut riippuvuudet Lisätään seuraavaksi riippuvuus yksinkertaiselle autoloaderille
Composer.json 16 Luodaan projektin juureen composer.json tiedosto Kirjoitetaan JSON objekti autoload ja määritetään sille classmap taulukko Tässä ladataan koko projektin sisältä kaikki luokat automaattisesti HUOM! PSR-0 autoload standardi on vanhentunut ja korvattu PSR-4 autoload standardilla Harjoituksena muuttaa PSR-0 -> PSR-4
Composer install 17 Ajetaan komentorivillä composer install Tämä lukee composer.json tiedoston ja asentaa vendor hakemistoon projektin riippuvuudet. Tässä autoload.php tiedoston Voidaan poistaa require:t kaikilta luokkatiedostoilta ja ladata vain autoload.php Composer rakentaa autoload_classmap.php tiedoston, jossa mapataan nimiavaruudellinen luokka sen toteuttavaan tiedostoon Tämä ei päivity automaattisesti. Jos siirtelet tiedostoja hakemistosta tai nimiavaruudesta toiseen, tai lisäät tiedostoja, sinun tulee uudelleen generoida classmap ajamalla komentorivillä composer dump-autoload
Composer.json ja composer.lock 18 Composer.json määrittää paketin projektin riippuvuudeksi Composer.lock sitoo riippuvuuden tiettyyn versioon Kun uusi kehittäjä ajaa composer install, composer.lock varmistaa, että hän saa juuri saman version komponentista, vaikka uudempikin olisi tarjolla Tämä ehkäisee mahdollisia versioyhteensopivuusongelmia Composer update päivittää riippuvuudet uusimpiin versioihin Composer.lock luodaan uudestaan viittaamaan ajohetken uusimpaan versioon Lisää siis molemmat tiedostot versionhallintaan!
Autoload 19 käytössä Nyt voidaan poistaa bootstrap.php tiedostosta require:t ja korvata ne vendor/autoload.php:lla Kuvassa luokat jaoteltu nimiavaruuksiin Päänimenä geneerinen App Framework spesifi koodi App\Core -nimiavaruudessa
Composer packages & Symfony 20 Vaihdetaan aikaisempi Request luokka paljon kypsempään toteutukseen Symfony on PHP Framework, mutta myös kokoelma tarkkaan valittuja komponentteja, joita monet muut kirjastot hyödyntävät https://symfony.com/components Halutaan lisätä symfonyn http-foundation paketti, joka määrittää Request luokan, joka tarjoaa paremman API:n PHP:n superglobaaleille ($_GET, $_POST, $_SERVER, ) Ajetaan komento composer require symfony/http-foundation Tämä lataa tarvittavat tiedostot vendor hakemistoon ja kirjoittaa composer.json tiedostoon sen projektin riippuvuudeksi Näin varmistetaan, että jokaisella kehittäjällä on samat paketit asennettuna Koska näitä ulkopuolisia paketteja ei kuulu sisällyttää projektin versionhallintaan
Vaihdetaan luokan toteutus toiseen 21 Muista composer dumpautoload Käytetään symfonyn Request luokkaa Luodaan $request objekti superglobaaleista (luettavuuden vuoksi. Todellisuudessa inline) Annetaan reitittimelle uuden luokan luoma pyyntö
Composer ja versionhallinta 22 Laitetaan versionhallintaan vain projektin oma koodi Ne komponentit, joita hyödynnetään, listataan composer.json tiedostossa Jos uusi kehittäjä liittyy projektiin, hän kloonaa repositoryn ja ajaa composer install saadakseen täsmälleen samat versiot vaadituista riippuvuuksista Riippuvuuskomponenteilla on omat repositorynsä, ne eivät kuulu tämän projektin versionhallintaan Riippuvuuskomponentteihin ei tästä syystä tehdä suoraan muutoksia Koska muuten uudempaan versioon siirtyminen ylikirjoittaisi tekemämme muutokset Muutokset toteutetaan esimerkiksi perimisen kautta
Kontrollerien refaktorointi ja reititys 23
Halutaan kontrollerit luokiksi 24 Nyt kontrolleri on vain pätkä koodia Halutaan refaktoroida se luokaksi, jolla on metodeja Halutaan reitittää pyyntö tiettyyn kontrolleriin ja tiettyyn metodiin Tiedostetaan, että voidaan tehdä lukupyyntö GET tai kirjoituspyyntö POST Esim. Lomake lähettää tallennettavat tiedot POST /todos Joka johtaa TodosController->save() metodiin Tätä metodia ei tarvitse, eikä saa kutsua luettavaksi, se vastaa vain POST pyyntöön GET /todos johtaa TodosController->index() metodiin RESTful routes
25 RESTful routes https://en.wikipedia.org/wiki/re presentational_state_transfer Erityisen tärkeä toteutettaessa avoimia rajapintoja, koska rajapinnan hyödyntäjä voi olettaa sovitun reititysmallin Mutta luontevaa toteuttaa myös sisäisessä reitityksessä, koska tämä on yleisesti oletettu nimeämiskäytäntö TIETA12-2017 - A.S.
Miten monta tasoa on liikaa? 26
Reitit täytyy määritellä uudestaan 27 Sovitaan API: REITITIN->[METODIN NIMI]( polku, Kontrolleri@metodi ); $router->get( /todos, TodosController@index ); $router->post( /todos, TodosController@save ); Muutetaan routes.php käyttämään tätä API:a
Reititintä täytyy 28 refaktoroida Jotta voimme ottaa huomioon pyynnön tyypin, muutetaan $routes taulukko assosiatiiviseksi tyypin mukaan $routes[ GET ][ /todos ] = TodosController@index ; Koska routes.php sisältää kutsuja, ladataan tiedosto Router luokan sisällä Router palauttaa luokan, sitä ei enää tarvitse ladata erikseen
Reittien 29 tallentaminen
30 Reitittäminen TIETA12-2017 - A.S.
31 Metodin kutsuminen Huomaa tuplakenoviiva Koska yksi kenoviiva toimii escape :na varatuille merkeille (\{), laitetaan kaksi \\ => \ Tästä syystä osa käyttää aina kaksinkertaista kenoviivaa, jolloin asiaa ei tarvitse miettiä TIETA12-2017 - A.S.
Kontrolleri 32 luokaksi Nyt voidaan jakaa toiminnallisuus kontrolleri-luokille Reititin päättää mitä luokkaa ja luokan metodia kutsutaan pyynnön polun ja tyypin perusteella
Reititin käytännössä 33
Resurssikontrolleriksi 34
Halutaan tallentaa tietoja järjestelmään Siirretään Request luokka DI containeriin App::bind('request', Request::createFromGlobals()); Lisätään POST reitti $router- >post( /todos, TodosController@save ); Lisätään TodosController luokkaan save() metodi Asetetaan lopuksi vastauksen header, joka pakottaa uudelleenohjauksen 35.htaccess:n kanssa voidaan ohjata suoraan /todos
Resurssin 36 lisääminen Lomake lähettää tyypillä POST polkuun /todos Päädytään siis tallennusmetodiin Kentän nimi vastaa parametria TIETA12-2017 - A.S.
Tietoa muokkaavat reitit ovat POST 37 reittejä HTTP tarjoaa verbit GET, POST, PUT/PATCH, DELETE, Nämä vastaavat hyvin Read, Create, Update ja Delete (CRUD) Mutta selain tällä hetkellä ymmärtää vain GET ja POST kutsuja Voidaan lisätä lomakkeelle piilokenttä method=delete Ohjelma voidaan laittaa reagoimaan samalla tavalla lomakkeen simuloituun DELETE -pyyntöön ja joltain muulta asiakkaalta tulevalle oikealle DELETE pyyntöön Usein kuitenkin toteutetaan myös POST /delete-item, $_POST[ id ] Tietoa muokkaavat reitit määritellään POST reiteiksi XSS haavoittuvuuksien välttämiseksi (tästä lisää seuraavalla luennolla) Nullipotent ja idempotent -toiminnot
Kielen ominaisuuksia - traits 38
Trait 39 Trait on joukko ominaisuuksia, joita voidaan liittää luokkaan Vertaa mixin Rubyssä Vaihtoehto perinnälle siinä tapauksessa, että luokilla ei ole luontevaa yhteistä yläluokkaa Koska kielessä ei ole tuke moniperinnälle Esimerkiksi kauppa ja auto. Näillä ei ole helppoa yhteistä yläluokkaa, mutta molemmilla on jokin sijainti ja molemmilla voisi olla metodi, joka muuttaa sijainnin katuosoitteeksi $car->getstreedaddrs() ja $shop->getstreetaddrs()
Trait -esimerkki 40 Trait tuo ominaisuuksia ja metodeja luokkiin, joihin se liitetään Metodia voidaan kutsua luokan instanssin kautta aivan kuin se olisi luokan oma metodi Trait mahdollistaa toistuvan toiminnallisuuden liittämisen eri luokkiin ilman perintää ja toistamatta samaa koodia useassa kohdassa Huomaa use sanan käyttö. Luokan ulkopuolella se viittaa nimiavaruuteen, luokan sisällä trait:in DRY Don t repeat yourself
Traits esimerkki 2 41 Halutaan, että aina kun tietomalliluokan tieto muuttuu, siitä kirjoitetaan muutostieto tietokantaan. Mutta sovitaan, että vain tiettyjen tietomalliluokkien muutokset ovat ilmoittamisen arvoisia, emme halua tietoa kaikista tietomalliluokista. Voidaan lisätä tietomalliluokkaan trait Loggable, joka tarjoaa toiminnallisuuden muutostietojen tallentamiseen. Tai halutaan, että joitain tietomalliluokkia voidaan hakea rajapinnan kautta JSON muodossa. Voidaan lisätä vain haluttuihin tietomalliluokkiin trait JsonSerializable.
Kielen ominaisuuksia anon. funktiot 42
Anonyymi funktio 43 On yleensä metodin callback funktio Esimerkiksi array_map käy läpi taulukon arvot ja palauttaa kunkin arvon funktion läpi ajettuna
Anonyymi funktio 44 Anonyymi funktio ei tiedä ulkopuolisesta maailmasta Tämä koodi ei toimi, koska muuttujaa $multiplier ei tunneta anonyymin funktion sisäisessä maailmassa
Jälleen use - avainsana 45 Lisäämällä anonyymiin funktioon use($var), voidaan tuoda sen sisäiseen todellisuuteen ulkopuolisia muuttujia Muuttujan arvo sulkeuman sisällä on pysyvästi se, mikä se oli kutsumisen hetkellä
Kielen ominaisuuksia - generaattori 46
Generaattorit 47 Normaalisti iteroitava entiteetti, vaikka taulukko, ladataan kokonaan muistiin Esimerkki: luodaan halutun kokoinen taulukko, joka täytetään juoksevilla numeroilla ja tulostetaan sitten jokaisen solun arvo
Generaattorit 48 Jos parametrina on 15, ohjelma useimmiten toimii Mutta jos parametrina on tarpeeksi suuri luku, lopulta palvelimen varaama muisti loppuu kesken Jos numeron sijaan käsiteltäisiin tiedoston rivejä, ongelma olisi todennäköisempi (siis jos emme tiedä tiedoston kokoa) Jos suoritukselle on varattu 1gb muistia ja tiedoston koko on 2gb Muistiin ei voida ladata enempää tietoa, kuin mitä sinne mahtuu Generaattori lukee muistiin vain yhden iteraation kerralla Mutta samalla iterointi on vain yhdensuuntainen, eikä iteraatioiden yli voi hyppiä, kuten normaaleilla iteroitavilla
yield 49 Yield palauttaa yhden iteraation kerralla, muistia varataan vain yhdelle Iterointia jatketaan viimeiseen yield:n asti tai siihen saakka, kunnes vastaan tulee return
Generaattori voidaan myös keskeyttää 50 Kesken iteroinnin voidaan syöttää arvo $generator- >send() ja ottaa se vastaan $injected = yield $i; Voisi olla hyödyllinen isoa CSV tiedostoa parsiessa Jos jokin ehto täyttyy, tiedostoa ei tarvitse käsitellä loppuun
Arkkitehtuurista ja riippuvuuksista 51
Spaghetti Coupling 52
OOP Coupling 53
SOLID 54 Purettu riippuvuus konkreettiseen toteutukseen Code to an interface Open to extension / closed for modification Dependency inversion Single responsibility Voidaan yksikkötestata vaihtamalla tietolähde Division of concerns
Kysymyksiä? 55