5

Сокращенный тернарный оператор в PHP

Тернарный оператор — вещь известная и скучная:


$a = $expr1 ? $expr2 : $expr3;

Если $expr1 истинно, результатом $a станет $expr2, иначе $expr3.

А теперь об интересном! Начиная с версии PHP 5.3 можно опустить $expr2.


$a = $expr1 ?: $expr3;

Это равносильно записи:


$a = $expr1 ? $expr1 : $expr3;

Примеры:


$a = true ?: false; // true

$a = false ?: true; // true

$a = 1 ?: 2; // 1

$a = 0 ?: 2; // 2

В мануале об этом упоминается очень сухо и коротко. Ну а что еще добавить?

7

Кодировки при работе с DOMDocument

DomDocument — мощный инструмент работы с XML и HTML с использованием методов DOM. Главное при его использовании не запутаться в кодировках.

Конструктор DOMDocument принимает два параметра — версию и кодировку документа

$dom = new DOMDocument([ string $version [, string $encoding ]]);

А чтобы начать работать с существующим HTML-документом его надо загрузить в объект DOMDocument:

$dom->loadHTML( string $source );

Проблема в том, что документ загружается совсем не в той кодировке, что указана в конструкторе. Вообще, параметры конструктора никак не влияют на загруженный документ. А используются только при методах save():

$htmlString = $dom->saveHTML ();

Так что параметры конструктора можно совсем опустить, если вы собираетесь только работать с существующим деревом и не планируете его изменять и выгружать.

При загрузке же кодировка определяется только исходя из содержимого, загружаемого документа. При этом достаточно часто объект не может определить кодировку верно. Поэтому желательно указывать ее вручную. Возможны два способа. Добавить к импортируемому документу строку кодировки


$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $source);

Либо сконвертировать все символы документа в HTML-entities перед импортом


$source = mb_convert_encoding($source, 'HTML-ENTITIES', 'utf-8');
$dom->loadHTML($source);

Первый вариант кажется предпочтительнее в плане производительности. В плане результатов работы различий замечено не было.

Если работать с DOM планируется много, то удобно оформить эти конструкции в отдельный класс наследника DOMDocument, добавить в методе загрузки дерева второй параметр кодировки и определить метод, например, так:


class MyDOMDocument extends DOMDocument {
    public function loadHTML($source, $encoding) {
        $source = '<meta http-equiv="Content-Type" content="text/html; charset='.
            $encoding.'">' . $source
        $dom->loadHTML('' . $source);
    }
}
0

Позднее статическое связывание в PHP

В PHP 5.3 появилась такая интересная возможность, как позднее статическое связывание (late static binding). Дальше немного вольный перевод описания из официального мануала.

Начиная с PHP 5.3.0 в языке реализована возможность, называемая поздним статическим связыванием, которая может использоваться для ссылки на вызываемый класс в контексте статического наследования.

Эту возможность назвали «позднее статическое связывание». «Позднее связывание» говорит о том, что static:: будет разрешаться не относительно класса, где определен метод, но будет вычисляться во время выполнения. «Статическое связывание» означает, что оно может быть использовано в вызовах статических методов (но не ограничивается только ими).

Ограничения self::

Статические ссылки на текущий класс в виде self:: или __CLASS__ разрешаются относительно класса, к которому относится функция, то есть относительно класса, где ссылка была определена:

Пример № 1: использование self::

<?php
class A {
  public static function who() {
    echo __CLASS__;
  }
  public static function test() {
    self::who();
  }
}

class B extends A {
  public static function who() {
    echo __CLASS__;
  }
}

B::test();
?>

Пример выведет:

A

Использование позднего статического связывания

Позднее статическое связывание пытается решить это ограничение, вводя ключевое слово, ссылающееся на класс, первоначально вызванный в процессе выполнения. То есть, ключевое слово, которое позволит сослаться на B из test() в предыдущем примере. Было решено не вводить новое слово, а использовать уже зарезервированное static.

Пример № 2: простое использование static::

<?php
class A {
  public static function who() {
    echo __CLASS__;
  }
  public static function test() {
    static::who(); // здесь происходит позднее статическое связывание
  }
}

class B extends A {
  public static function who() {
    echo __CLASS__;
  }
}

B::test();
?>

Пример выведет:

B

Замечание: static:: не работает как $this для статических методов! $this-> следует правилам наследования, а static:: нет. Это различие уточняется ниже.

Пример № 3: использование static:: в нестатическом контексте

<?php
class TestChild extends TestParent {
  public function __construct() {
    static::who();
  }

  public function test() {
    $o = new TestParent();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}

class TestParent {
  public function __construct() {
    static::who();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}
$o = new TestChild;
$o->test();
?>

Пример выведет:

TestChild
TestParent

Замечание: Позднее статическое связывание останавливает процесс разрешения вызова. Статические вызовы с использованием ключевых слов parent:: или self:: передают информацию о вызове дальше.

Пример № 4: Передача и непередача вызовов

<?php
class A {
  public static function foo() {
    static::who();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}

class B extends A {
  public static function test() {
    A::foo();
    parent::foo();
    self::foo();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}
class C extends B {
  public static function who() {
    echo __CLASS__."\n";
  }
}

C::test();
?>

Пример выведет

A
C
C

Крайние случаи

Существует множество различных способов вызвать метод в PHP, такие как коллбэки или магически методы. Поскольку позднее статическое связывание разрешается во время выполнения, это может привести к неожиданным результатам в так называемых крайних случаях.

Пример № 5 Позднее статическое связывание в магических методах

<?php
class A {
  protected static function who() {
    echo __CLASS__."\n";
  }

  public function __get($var) {
    return static::who();
  }
}

class B extends A {
  protected static function who() {
    echo __CLASS__."\n";
  }
}

$b = new B;
$b->foo;
?>

Пример выведет:

B

Пару статей по теме на хабре: Singleton и Late static binding и Позднее статическое связывание в PHP.

Copyright © 2017 — dec5e | Site design by Trevor Fitzgerald