Mennäänpä vähän abstrakteihimpiin asioihin, eli on funktio, joka hakee dataa tietokannasta. Tämän funktion paluuarvoa käytetään toisen funktion argumenttina datan hakemiseen tietokannasta. Esimerkiksi ensimmäisellä funktiolla haetaan käyttäjien id:t, joita tietty käyttäjä seuraa. Toiselle funktiolle annetaan argumentiksi tämä taulukko, jossa siis on käyttäjien id:itä. Toinen funktio palauttaa käyttäjän seuraamien käyttäjien viestit. Normaalisti ensimmäinen funktio palauttaa arrayn, jonka alkiot ovat käyttäjien id:itä. Jos tietokannasta ei löydy mitään, mitä pitäisi palauttaa? False, Null vai tässä tapauksessa peräti tyhjä array? Itse olen oikeastaan sitä mieltä, että on melkein sama mitä palauttaa kunhan tarkistaa, mitä funktio on palauttanut ja toimii paluuarvon mukaan. Esim. jos ensimmäinen funktio palauttaa Falsen, toinen funktio toteaa, että tuli False, toinen funktio palauttaa tyhjän arrayn.
Tämä on mielestäni hankalampaa, mutta taitaa olla kannattavampaa kuin yksinkertaisten ja selkeiden one-linereiden palauttaminen.
Eikös olisi selkeintä palauttaa tyhjä taulukko, koska false viittaisi epäonnistuneeseen suoritukseen funktiossa. Tällöin jatkotoimet olisivat jokaisessa tilanteessa samanlaiset. Mikäli dataa saadaan, niin käydään läpi taulukko, sekä haetaan vastaavat viestit. Tyhjällä taulukolla ei tule tietenkään yhtään viestiä, mutta taulukon läpikyvä rakenne (for, while, foreach) toimivat tyhjänkin taulukon kanssa fiksusti.
Falsen tai NULL-arvon kanssa kyseiset rakenteet eivät sen sijaan toimi järkevästi, joten kyseinen tilanne tulisi tutkia ennen rakenteeseen siirtymistä.
Minusta tyhjä taulukko on ainoa järkevä vaihtoehto juuri Teuron mainitsemasta syystä: silloin kutsujan ei tarvitse tarkistaa mitään, vaan tulos on joka tapauksessa taulukko. Toinen jollain tavalla mielekäs vaihtoehto olisi null, mutta siihen ei ole mitään syytä. Sen sijaan false on täysin epälooginen.
Voit miettiä asiaa verbaalisesti. Funktiokutsu on sama kuin vaikkapa kysymys "mitä käyttäjiä tietokannassa on". Tyhjä taulukko on sama kuin "löytyi 0 käyttäjää". Null on sama kuin "ei löytynyt". False on sama kuin "väärin" tai "ei ole totta". Mitkä vastaukset vaikuttavat järkeviltä?
Voit myös miettiä, mitä hyötyjä ja haittoja keksit eri vaihtoehdoille. Enpä usko, että keksit juurikaan muita kuin tuon taulukon hyvyyden.
Kiitos pohdinnoistanne, tyhjä taulukko on näköjään paras vaihtoehto ja itsekin ajattelin sitä. Entäpä jos funktio palauttaa vain yhden arvon? Mikä vaihtoehto olisi silloin loogisin? Luulisin, että mahdollisesti false, koska esimerkiksi 0 tai "" ovat mahdollisia arvoja myös esim. tietokannan taulun kentille, ja palautusarvon kanssa voi tulla sekaannuksia.
Lisäys:Ja kun funktio palauttaa tyhjän arrayn, jota käytetään toisen funktion argumenttina, joka laitetaan SQL:n IN-kyselyyn, niin tulee SQL-error. Eli IN-kysely ei voi olla "tyhjä", IN() ei kelpaa, pitää olla IN(a,b). Tässä tapauksessa pitää kyllä tarkistaa, onko taulukko tyhjä.
Mielestäni jos sieltä voi tulla 0-n tietuetta, niin on loogista palauttaa x -pituinen taulukko silloinkin kun x on 0 tai 1.
Juuri jossain välissä tuli kiroiltua oikein huolella kun PHP:n joku web servicen käyttöimplementaatio palautti taulukon jos arvoja oli enemmän kuin yksi mutta paljaan arvon jos niitä oli yksi (vaikka wsdl:ssä on speksattu että taulukkoa saadaan). Mikään järjellinen systeemi ei toimi noin.
Tuo nollan pituinen taulukko SQL:ään on muutenkin sellainen joka kannattaa tarkistaa. Turha pyytää tietokannalta jotain jos tietää jo valmiiksi ettei sieltä tule mitään. (Paitsi tietty jos se on tyyliin blaa=7 or bloo in (...) )
Grez kirjoitti:
Turha pyytää tietokannalta jotain jos tietää jo valmiiksi ettei sieltä tule mitään
Minun tapauksessani sieltä voi tulla jotain, tokihan funktion alussa voi katsoa onko taulukossa jotain ja sen perusteella tehdä SQL-kysely tai olla tekemättä, koska tosiaan tuon SQL:n IN-kysely ei toimi, jos sinne annetaan tyhjä lista.
Ja tarkoitin oikeastaan, että jos on funktio joka palauttaa korkeintaan yhden arvon, niin kannattaako silloin palauttaa taulukko vai pelkkä arvo, ja jos pelkkä arvo on "tyhjä", niin kannattaako palauttaa false vai null, vai jotain muuta.
Ripe kirjoitti:
Minun tapauksessani sieltä voi tulla jotain
Tarkoitin nimen omaa sitä, että jos SQL-kyselyssäsi on ehto muotoa "WHERE Jotain IN ({taulukon sisältö})" niin tiedetään jo valmiiksi että kysely ei palauta mitään, jos taulukon pituus on nolla. Tai siis sen ei loogisesti kuuluisi palauttaa mitään sen lisäksi, ettei se edes ole validi kysely.
Grez kirjoitti:
Tarkoitin nimen omaa sitä, että jos SQL-kyselyssäsi on ehto muotoa "WHERE Jotain IN ({taulukon sisältö})" niin tiedetään jo valmiiksi että kysely ei palauta mitään, jos taulukon pituus on nolla.
Aivan totta, en näköjään tajunnut tuon lauseesi tarkoitusta heti. Ja tosiaan, tuo ei edes toimisi, jos taulukko on tyhjä, koska MySQL antaa virheen.
Jos suorituksessa ei ole tapahtunut virhettä, palautetaan samaa tietotyyppiä kuin aina muulloinkin. Virheen yhteydessä palautan aina nullin, koska falsejen evaluointi on php:ssä tehty liian työlääksi ja virheherkäksi.
Se ei voi mitenkään passata, että jos funktiolle antaa väärän tyypin parametrin, niin palautettu tulos olisi kuitenkin kelvollinen. On kiistelyn arvoinen asia, onko tyhjän taulukon antaminen funktiolle, joka periaatteessa vaatii ei-tyhjiä taulukoita, virhe vai ei. Itse sallin tyhjät taulukot ihan kätevyyden takia.
No, jos on funktio, joka normaalisti palauttaa käyttäjän nimen, ja jos käyttäjää ei löydy, mitä pitäisi palauttaa? Periaatteessahan tuossa toimisi tuo The Alchemistin käyttämä null, koska periaatteessahan se on virhe, että etsitään yhtä asiaa, mutta ei löydetä sitä. Taulukoiden kanssa on tosiaan eri asia, taulukko voi olla tyhjä, mutta tyhjää/alustamatonta muuttujaa ei ole järkevää palauttaa, koska sen arvo voi olla mitä vain. Tuossa on hieman hankala palauttaa "samaa tietotyyppiä", koska php:ssä muuttujilla ei ole tietotyyppiä, eli ei esimerkiksi voi palauttaa int:iä, koska sellaista tietotyyppiä ei voi määritellä muuttujalle.
Olen vähän eri mieltä. Null on hyvä paluuarvo ja pyydettyä arvoa ei ole asetettu ja se asettamattomuus on bisneslogiikan kannalta hyväksyttävää. Jos arvo pitäisi bisneslogiikan kannalta olla aina asetettu tai jos on annettu väärän tyyppinen parametri niin tulisi heittää poikkeus. Ensimmäisessä tapauksessa esim DomainException ja toisessa InvalidArgumentException. Tyhjä taulukko on toki parempi kuin null silloin kun arvojen löytymättömyys on hyväksyttävää ja muuten voitaisiin palauttaa useampi arvo. False sopii lähinnä silloin jos mitään loogista paluuarvoa ei ole mutta halutaan kertoa esim että onnistuiko operaatio vai ei ja toki myös silloin kun paluuarvo on boolean tyyppinen. Usein näkee myös että metodit joilla ei ole mitään loogista paluuarvoa, palautaavat olion itse, jotta voidaan ketjuttaa metodikutsuja (esim. Setterit)
Ripe: php:ssä on tietotyyppejä vaikka millä mitoin. Intit, floatit, stringit ja taulukot ja oliotkin löytyvät. Lisäksi muuttuja voi olla tyypiltään funktio tai "callable", joka voi olla funktion ohella pari (olio, metodi). Korkean tason ohjelmointi olisi yleisesti ottaen täysin mahdotonta, jos kieli ei tekisi eroa numeroiden ja merkkien (merkkijonojen) saati erikoisempien tyyppien välillä.
Jos funktion on tarkoitus palauttaa merkkijonoja tai numeroita, mutta kutsun tuloksen pitäisi olla ei-kelvollinen, niin silloinhan tietysti palautetaan ei-kelvollinen arvo. Itse siis palauttaisin tällöin nullin.
Nullin palauttamista minäkin mietin. Ja tarkoitin tuolla tietotyypittömällä sitä, että muuttujalle ei voi helposti määritellä sen tyyppiä, niin kuin esimerkiksi C++:ssassa, näin:
$variable = "Jotain"; $variable2 = 2;
std::string variable = "Jotain"; int variable2 = 2;
Ja kyllähän php:ssäkin muuttujat tyypitetään, tosin "duck typing"-tyylisellä menetelmällä.
Heikolla tyypityksellä ei ole vaikutusta siihen, voiko funktio palauttaa jotain tiettyä tyyppiä olevia arvoja. Muuttuja ei myöskään voi olla alustamaton php:ssä, vaan arvo on aina null, jos muuta arvoa ei määritellä. "Tyhjä muuttuja" on mielestäni ihan eri asia kuin alustamaton muuttuja. Tyhjyys voi tarkoittaa nullin ohella tyhjää merkkijonoa ja joskus myös muita asioita. Alustamaton muuttuja tuskin edes voi olla tyhjä.
The Alchemist kirjoitti:
vaan arvo on aina null, jos muuta arvoa ei määritellä
Ohops, tuota en tiennytkään. Luulin, että esiteltäessä muuttuja saa satunnaisen arvon, kuten esimerkiksi C++:ssassa. Ehkä ei tosiaan kannattaisi luulla mitään, vaan tarkistaa asia. Olisiko siis esimerkiksi tällainen funktio järkevä:
function findByName($name){ // SQL-kysely if ($result !== NULL){ // Jos tätä ei ole, tulee kohdassa $result->id virhe: trying to get property of non-object return $result->id; // Esimerkiksi näin, tämä on nopea esimerkki } return NULL; }
Ja, pitäisikö tuon palautusarvo tarkistaa funktiossa, josta kutsutaan findByName():a, vai funktiossa, jolle tuo annetaan argumentiksi? Jälkimmäisessä tapauksessa kai kannattaisi heittää InvalidArgumentException, jos argumentti olisi null. Eli siis pitääkö arvo tarkistaa esimerkkini mukaan index_action-funktiossa vai $this->post->getMessagesById()-funktiossa?
Ripe kirjoitti:
Ja, pitäisikö tuon palautusarvo tarkistaa funktiossa, josta kutsutaan findByName():a, vai funktiossa, jolle tuo annetaan argumentiksi?
Minusta tähän ei ole mitään yleispätevää sääntöä vaan asia riippuu aina tapauksesta.
Ripe kirjoitti:
Jälkimmäisessä tapauksessa kai kannattaisi heittää InvalidArgumentException, jos argumentti olisi null. Eli siis pitääkö arvo tarkistaa esimerkkini mukaan index_action-funktiossa vai $this->post->getMessagesById()-funktiossa?
Metodien nimet taitaa olla tässä pikkaisen epäselviä. Oletan että niiden todelliset merkitykset ovat "findIdByName" ja "getMessagesByUserId". Jälkimmäisessä metodissa voi varmaankin olla seuraavat tilanteet joissa palautettavia viestejä ei löydy: 1. annettu käyttäjä-id on olemassa mutta käyttäjällä ei ole yhtään viestiä, 2. annettua käyttäjä-id:tä vastaavaa käyttäjää ei ole ja 3. annettu käyttäjä-id on kokonaan vääränmuotoinen (esim null tms epäsopova id-arvo). Missään näissä tapauksissa yhtään viestiä ei voida paluttaa. Periaatteessa kohta 1. on aivan normaali, mutta tiukasti tulkittuna kohdat 2 ja 3 voisivat olla virhetilanteita. Käsittely riippuu paljon siitä että haluatko erotella nuo tapaukset toisistaan ja toimia jotenkin eri tavalla niissä vai riittääkö sinulle että toimit kaikissa tilanteissa samalla tavalla eli näytät esim sivun jossa sanotaan että viestejä ei löytynyt. Jos haluat erotella tilanteet jossa käyttäjää ei löydy ja jossa käyttäjällä ei ole viestejä niin tee null-tarkistus tuolle findByName-metodin paluuarvolle ennen kuin kutsut getMessagesById-metodia. Minusta poikkeusta ei tässä esimerkissä tulisi heittää, koska alkuperäinen syöte tulee selaimelta ($_GET["name"]) joten kaikkeen mahdolliseen syötteeseen tulisi varautua.
Kuten sanoin, koodini oli nopea esimerkki. Muistelin metodien nimet väärin, en katsonut oikeaa koodiani ollenkaan kirjoittaessani tuota. Oikeat metodien nimet ovat findByUserId ja findByName palauttaakin oikeasti koko käyttäjä-sarakkeen tiedot, ei pelkkää id:tä, eli metodien nimet ovat melko perusteltuja.
Esimerkiksi Doctrine, joka sattumoisin käyttää findBy-nimeämismallia, palauttaa nullin, jos haetaan yksittäistä oliota ehdoilla, joilla ei löydy mitään. Toinen vaihtoehto olisi lisätä luokkiin isNull()-funktio, jolla voi tarkistaa, onko olio oikeasti "joku" vai onko se tyhjä.
Olen myös nähnyt erään viritelmän, jonka on tarkoitus abstrahoida pois yleisimmät nulliin liittyvät tarkastelut: PHP Option
Kiitos The Alchemistille ja kaikille muille asioiden selvennyksestä, alan varmaan palautella nulleja, jos funktio ei löydä mitään, koska se vaikuttaa parhaalta ja loogisimmalta tavalta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.