PHPowe ciekawostki składniowe

Od jakie­goś miesiąca defi­ni­tyw­nie prze­sia­dłem się na PHP 5.4. Napi­szę dzisiaj o kilku cieka­wost­kach i nowych funk­cjach, które ułatwią nam progra­mi­styczne życie (mówię „nam” z tej okazji, że od 1 sierp­nia zmie­ni­łem pracę i jestem oficjal­nie zawo­do­wym progra­mi­stą :-) Zacznę jednak od nie do końca zrozu­mia­łego skró­co­nego opera­tora trój­ar­gu­men­to­wego dostęp­nego od wersji 5.3.

Wszy­scy na pewno znacie typowy opera­tor trój­ar­gu­men­towy (ang. ternary opera­tor): (waru­nek) ? (gdy prawda) : (gdy fałsz). Nie wszy­scy jednak korzy­stają z uprosz­czo­nej wersji (ang. shor­thand TO): (waru­nek) ?: (gdy fałsz). Jest to rzecz często spoty­kana w kodzie źródło­wym Symfony 2.

Zazwy­czaj spoty­kamy taką konstrukcję:

$var = true;
$var = $var === true ? 'true' : 'false';
// wynik to oczywiscie 'true'

Co jeżeli chcemy skorzy­stać z uprosz­czo­nej wersji?

// jesli $var ma wartosc false, '', null, []
// to wynikiem nastepnej operacji bedzie 'default value'
$var = $var ?: 'default value';

Jest to przy­datna konstruk­cja jeśli chcemy mieć pewność, że zmienna ma zawsze jakąś usta­loną wartość. Chroni nas to przed setkami instruk­cji spraw­dza­ją­cych czy mamy jakieś nulle, falsy i puste stringi :-)

Wywo­ły­wa­nie metod bezpo­śred­nio po instruk­cji new ma częste zasto­so­wa­nie np. wtedy, gdy właści­wie nie potrze­bu­jemy samej instan­cji obiektu, a tylko wynik konkret­nej metody. Przykład:

(new DateTime())->format('Y-m-d');

W jednej linijce zała­twiamy sprawę. Waru­nek to otocze­nie new nawia­sami okrągłymi.

Kolejna nowa możli­wość 5.4 to bezpo­śred­nie dobra­nie się do rezul­tatu dzia­ła­nia funk­cji zwra­ca­ją­cej tablicę. Przy­pu­śćmy, że nie inte­re­suje nas wynik dzia­ła­nia funk­cji, a tylko fakt czy poja­wił się jakiś błąd, np.

function foo() {
  return ['result' => 'some result',
          'error'  => 'some nasty error'];
}
echo foo()['error'];
// wynik to 'some nasty error'

Wg mnie ta konstruk­cja poten­cjal­nie zaciem­nia zrozu­mie­nie dzia­ła­nia kodu, gdyż może nam umknąć, że wynik funk­cji został „przy­cięty” do jednego klucza. Poza tym może suge­ro­wać, że funk­cja jest źle zapro­jek­to­wana skoro często odwo­łu­jemy się tylko do części jej „produktu”.

Od 5.4 możemy wymu­sić typ callable argu­mentu prze­ka­zy­wa­nego do funk­cji. Dodat­kowo zade­mon­struję kilka sposo­bów wywoływania:

function callMe (callable $callable) {
  echo $callable();
}
 
$callableFunction = function() {
  return 'im callable function';
}; // pamietajcie o sredniku na koncu!
 
callMe($callableFunction);
// wynik 'im callable function'
 
Class Foo {
 
  private $var = 'instance value';
 
  public static function bar() {
    return 'Foo::bar called';
  }
 
  public function baz() {
    return $this->var;
  }
}
 
callMe(['Foo', 'bar']);
// wynik 'Foo::bar called'
 
callMe([new Foo(), 'baz']);
// wynik 'instance value'
 
//dozwolona jest rowniez konstrukcja
$funcName = 'bar';
echo Foo::$funcName();

Stoso­wa­nie static w funk­cjach to dosyć fajna sprawa. Nie ważne ile razy wywo­łamy funk­cję, może ona zacho­wać swój stan. Ja wyko­rzy­stuję tę tech­nikę przy robie­niu testów jednost­ko­wych żeby mieć pewność, że funk­cja zosta­nie wywo­łana tyle razy ile prze­wi­dzia­łem żeby była wywo­łana. Poni­żej zade­mon­struję funk­cję z „rucho­mym” licz­ni­kiem wykonań.

function counter ($maxCallNumber = 3) {
  static $funcCallNumber = 0;
  $funcCallNumber++;
  if ($funcCallNumber > $maxCallNumber) {
    throw new Exception('I was called too many times');
  }
  return 'counter function called' . PHP_EOL;
}
 
echo counter();
echo counter();
echo counter();
echo counter(4);
// gdyby nie przestawienie na 4 w ostatnim wywolaniu,
// mielibysmy do czynienia z wyjatkiem

Ostat­nią rzeczą, o której dzisiaj wspo­mnę jest funk­cja session_status(). Jeśli mamy przed sobą zada­nie stwo­rze­nia obsługi sesji użyt­kow­nika, za pomocą tej funk­cji możemy to wygod­nie zrobić.

switch (session_status()) {
  case PHP_SESSION_DISABLED:
    throw new Exception('Zablokowane tworzenie sesji');
    break;
  case PHP_SESSION_NONE:
    startSession();
    break;
  case PHP_SESSION_ACTIVE:
    // sesja istnieje, mozna smialo korzystac z $_SESSION
  break;
}

Podobne wpisy:

  1. Przy­kład wyko­rzy­sta­nia domknięć w PHP
  2. Wzorzec projek­towy memento w PHP
  3. Wali­duj z Harrym Potterem
  4. Play­ing with PHP func­tio­nal way

4 Comments

  • 11 sierpnia 2012 - 18:19 | Permalink

    Przy­znam, że długo zazdro­ści­łem pytho­nowi możli­wo­ści odwo­ła­nia się do wybra­nego elementu zwra­ca­nej warto­ści funk­cji, jak piszesz tutaj:
    echo foo()['error'];
    i wielo­krot­nie wkurzało mnie pisa­nie w dwóch krokach — przy­kład z kosmosu:

    $user = $users->getUser($id);
    $email = $user['email'];

    teraz będzie krócej:
    $email = $users->getUser($id)['email'];
    Na to czekałem :-)

  • 12 sierpnia 2012 - 11:49 | Permalink

    @mheki
    Już widzę, jak niektó­rzy robią tak:

    $username = $users->getUser($id)['username'];
    $password_hash = $users->getUser($id)['password_hash'];
    $email = $users->getUser($id)['email'];

    katu­jąc bazę 2 zbęd­nymi zapytaniami ;-)

    Oczy­wi­ście w uzasad­nio­nych przy­pad­kach nie mam nic prze­ciwko. Praw­do­po­dob­nie mamy mini­malny zysk czasowy dla CPU z okazji tego, że nie musi tworzyć zbęd­nej zmiennej.

  • 13 sierpnia 2012 - 15:18 | Permalink

    @Śpiechu no ale w twoim przy­kla­dzie $users->getUser() az prosi sie o cache, wiec problem raczej bedzie (powi­nien byc!) marginalny ;)

    PS super podsu­mo­wa­nie nowo­sci, wszytko jakos znam i uzywam, ale calla­ble prze­ga­pi­lem ;) czyzby php szlo w asynchronicznosc ;)

  • melkor1984
    14 sierpnia 2012 - 10:33 | Permalink

    BTW: opera­tor trój­ar­gu­men­towy w tej krót­szej formie nosi nazwę „Elvis operator” :)

  • Dodaj komentarz

    Twój adres e-mail nie zostanie opublikowany.

    Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <p> <pre lang="" line="" escaped=""> <q cite=""> <strike> <strong>