PHP — язык достаточно взрослый. И как и у любого взрослого языка, PHP за время своего существования накопил богатый функционал. Возможностей у PHP настолько много, что знать обо всех практически невозможно. Но сегодня я познакомлю вас с некоторыми интересными и полезными, на мой взгляд, тонкостями.

Приведение типов

Как известно, PHP — нестрого типизированный язык. Это значит, что можно свободно преобразовывать один тип данных в другой (например, число в строку). Естественно, существует ряд четких правил для таких преобразований. Но далеко не все изучают эти правила досконально.

Например, не все знают, что если привести ассоциативный массив к типу object, то получим экземпляр класса stdClass, где ключи и значения массива станут именами и значениями свойств объекта соответственно:

$array = array(
    'prop1' => 'Hello',
    'prop2' => 'World'
);
$object = (object) $array;
var_dump($object);

В результате выполнения этого кода получим следующее:

object(stdClass)#1 (2) {
  ["prop1"]=>
  string(5) "Hello"
  ["prop2"]=>
  string(5) "World"
}

Можно выполнить и обратное преобразование: привести объект к типу array. В результате получим ассоциативный массив, в котором в качестве ключей и значений будут выступать свойства объекта и их значения соответственно:

$object = new stdClass;

$object->foo = 'bar';
$object->baz = 'qux';

$array = (array) $object;

var_dump($array);

Выполнив этот код, получим такой результат:

array(2) {
  ["foo"]=>
  string(3) "bar"
  ["baz"]=>
  string(3) "qux"
}

Исключением из такого поведения является встроенный класс ArrayObject. Если приводить к типу array экземпляры этого класса, то на выходе получим только значения, установленные через интерфейс ArrayAccess:

$object = new ArrayObject;

$object['foo'] = 'bar';

$object->baz = 'qux';

$array = (array) $object;

var_dump($array);

Результатом выполнения данного кода будет следующее:

array(1) {
  ["foo"]=>
  string(3) "bar"
}

В PHP есть возможность для любого класса объявить так называемый магический метод __toString(), который определяет, какое значение будет получено в случае преобразования объекта в строковое значение. Мы же можем использовать этот метод для преобразования объекта в числовое или булевое значение:

class CastTest
{
    protected $value;

    public function __construct($value)
    {
        $this->value = (string) $value;
    }

    public function __toString()
    {
        return $this->value;
    }
}

$object = new CastTest(4.2);
$bool = (bool) (string) $object;
$int = (int) (string) $object;
$float = (float) (string) $object;

var_dump($bool, $int, $float);

На выходе получаем такие значения:

bool(true)
int(4)
float(4.2)

Присвоение объектов по ссылке

Начиная с PHP версии 5, все объекты копируются по ссылке. Это значит, что при обычном присвоении переменной $A объекта из переменной $B, вместо копии объекта будет присвоена ссылка на объект. До этого в PHP существовала возможность копировать значения по ссылке. Для этого использовался оператор =&. Однако, не все знают, что в PHP 5 следующие две строки в контексте объектов не равнозначны:

$A =  $B;
$A =& $B;

Чтобы продемонстрировать, в чем отличие этих двух записей, выполним следующий код:

$B1 = (object) array('obj' => 1);
$B2 = (object) array('obj' => 2);

$A1 =  $B1;
$A2 =& $B2;

var_dump($A1, $A2);

$B1 = null;
$B2 = null;

var_dump($A1, $A2);

Получаем следующий результат:

object(stdClass)#1 (1) {
  ["obj"]=>
  int(1)
}
object(stdClass)#2 (1) {
  ["obj"]=>
  int(2)
}
object(stdClass)#1 (1) {
  ["obj"]=>
  int(1)
}
NULL

Работа с ini-файлами

Наверное, все знают о функции parse_ini_file(). Она позволяет преобразовать ini-файл в ассоциативный массив настроек. Однако, не все в курсе, что ini-файл позволяет использовать константы и переменные окружения PHP при указании значений ini-файла.

Создадим файл настроек:

class_path = DIRPATH "/classes"
signature = "My CMS at " ${COMPUTERNAME}
memory_limit = ${memory_limit}

Теперь напишем код, который будет считывать наш файл настроек:

define ('DIRPATH', dirname(__FILE__));

$settings = parse_ini_file('./settings.ini');

print_r($settings);

Результатом выполнения такого кода будет что-то вроде этого:

Array
(
    [class_path] => C:\Server\home\localhost\htdocs/classes
    [signature] => My CMS at ANTON-PC
    [memory_limit] => 128M
)

В документации упоминается про возможность обрабатывать константы в ini-файлах, а вот про переменные окружения (значения суперглобального массива $_ENV) и настройки из php.ini там не сказано ничего. Реальную ценность обычно представляют только константы.

Бонус: работа со строками и массивами с использованием фигурных скобок

Известно, что в PHP есть возможность работать с массивами и строками с использованием квадратных скобок. Однако мало кто знает, что фигурные скобки могут выполнять ту же самую роль:

$string = 'Hello world';
$array = range(1, 3);

var_dump($string[1], $string{1}, $array[2], $array{2});

Этот код выведет следующее:

string(1) "e"
string(1) "e"
int(3)
int(3)

Работа со строками и массивами с использованием фигурных скобок осталась для обратной совместимости с PHP 3. Я же узнал о ней, случайно наткнувшись на такую запись в очень старом коде.

На этом у меня все. В комментариях предлагаю делиться своими хитростями и малоизвестными фишками языка.