Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

Перейти на сайт->

Бесплатный Курс "Практика HTML5 и CSS3"

Освойте бесплатно пошаговый видеокурс

по основам адаптивной верстки

на HTML5 и CSS3 с полного нуля.

Начать->

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

Верстайте на заказ и получайте деньги.

Получить в подарок->

Бесплатный курс "Сайт на WordPress"

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

Получить в подарок->

*Наведите курсор мыши для приостановки прокрутки.


Определение типов объектов в объектно-ориентированном программировании на PHP

Перед изучением данной статьи вы можете прочитать предыдущую статью из этой серии - "Работа с методами в объектно-ориентированном программировании на PHP".

Типы определяют, каким способом можно оперировать данными в сценариях.

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

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

Класс также определяет тип имени себя, но на более высоком уровне. Поэтому объект ShopProduct относится к элементарному типу object, а также к типу класса ShopProduct. В данной статье мы рассмотрим обе разновидности типов в отношении методов класса.

При определении метода и функции не требуется, чтобы аргумент был определенного типа. Но это одновременно и преимущество, и недостаток.

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

Но эта гибкость, с другой стороны, может стать причиной неопределенности в коде, когда тело метода ожидает один тип аргумента, а получает - другой.


Элементарные типы

PHP является слабо типизированным языком. Это означает, что нет необходимости объявлять тип данных, который должна хранить переменная. Так, в пределах одной и той же области видимости переменная $number может содержать как значение 2, так и строку "two" ("два").

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

Это не означает, что в PHP нет понятия типа. Каждое значение, которое можно присвоить переменной, имеет свой тип. Вы можете определить тип значения переменной с помощью одной из функций проверки типов языка PHP.

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

- is_bool(); Boolean; Одно из двух значений: true или false (истина или ложь)
- is_integer(); Integer; Целое число
- is_double(); Double; Число с плавающей точкой (десятичное число)
- is_string(); String; Символьные данные
- is_object(); Object; Объект
- is_array(); Array; Массив
- is_resource(); Resource; Дескриптор, используемый для идентификации и работы с внешними ресурсами, такими как базы данных или файлы
- is_null(); Null; Неинициализированное значение

Проверка типа переменной особенно важна, когда вы работаете с аргументами в методе или функции.


Пример использования элементарных типов данных

Имейте в виду, что вы должны внимательно следить за типами данных в коде. Давайте рассмотрим пример одной из многих проблем, связанных с типами, с которой вы можете столкнуться.

Предположим, вам нужно извлечь параметры конфигурации из XML-файла. XML-элемент <resolvedomains> говорит приложению о том, следует ли пытаться преобразовать IP-адреса в доменные имена. Надо отметить, что такое преобразование полезно, однако отнимает много времени. Вот фрагмент XML-кода.


<settings>
	<resolvedomains>false</resolvedomains>
</settings>

Приложение извлекает строку "false" и передает ее в качестве параметра методу outputAddresses(), который выводит данные IP-адресов. Вот определение метода outputAddresses().


class AddressManager {
    function __construct() {
        private $addresses = array( "216.109.112.135", "64.233.187.99" );
    }

    function outputAddresses( $resolve ) {
        foreach ( $this->addresses as $address ) {
            print $address;
            if ( $resolve ) {
                print " (".gethostbyaddr( $address ).")";
            }
            print "\n";
        }
    }
}

Как видите, метод outputAddresses() циклически проходит по массиву IP-адресов и выводит его элементы один за одним. Если значение аргумента $resolve равно true, то метод, кроме IP-адресов, выводит также доменные имена.

Давайте рассмотрим код, в котором может вызываться этот метод.


$settings = simplexml_load_file("settings.xml");
$manager = new AddressManager();
$manager->outputAddresses( (string)$settings->resolvedomains );

В этом фрагменте кода для получения значения элемента resolvedomains используется SimpleXML API. В нашем примере мы знаем, что это значение - текстовый элемент "false", и преобразуем его в строку, поскольку так советуют в документации по SimpleXML.

Но этот код будет работать не так, как вы того ожидаете. Передавая строку "false" методу outputAddresses(), мы точно не знаем, какой тип аргумента используется в методе по умолчанию. Данный метод ожидает булеву величину, которая может принимать значение true (истина) или false (ложь).

При выполнении условного оператора строка "false" на самом деле превратится в значение true. Все дело в том, что при выполнении проверки интерпретатор PHP "заботливо" преобразует непустое строковое значение в булево значение true. Поэтому


if ( "false" ) {
	// ...
}

эквивалентно


if ( true ) {
	// ...
}

Чтобы исправить эту ситуацию, существует ряд подходов.

Во-первых, можно сделать метод outputAddresses() менее требовательным, чтобы он распознавал строку и применял некоторые основные правила для преобразования ее в булев эквивалент.


class AddressManager...

    function outputAddresses( $resolve ) {
        if ( is_string( $resolve ) ) {
           $resolve = ( preg_match("/false|no|off/i", $resolve ) )?
                 false:true;
        }
        // ...
    }

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


/**
* Выводит список адресов.
* Если переменная $resolve содержит истинное значение (true),
* то адрес преобразуется в эквивалентное имя хоста.
* @param $resolve Boolean Преобразовать адрес?
*/

function outputAddresses( $resolve ) {
    // ...
}

В-третьих, можно сделать метод outputAddresses() строгим в отношении типа данных аргумента $resolve.


function outputAddresses( $resolve ) {
    if ( ! is_bool( $resolve ) {
		die ("Методу outputAddresses() требуется булев аргумент\n");
	}
// ...
}

Этот подход заставляет клиентский код обеспечить корректный тип данных для аргумента $resolve. Преобразование строкового аргумента в клиентском коде - более дружественный подход, но, вероятно, он может вызвать другие проблемы.

Обеспечивая механизм преобразования в методе, мы предугадываем контекст его использования и намерение клиента. С другой стороны, навязывая булев тип данных, мы предоставляем клиенту право выбора - преобразовывать ли строки в булев тип и какое слово какому значению будет соответствовать.

А между тем, метод outputAddresses() разрабатывался с целью решить конкретную задачу. Этот акцент на выполнении конкретной задачи при намеренном игнорировании более широкого контекста - важный принцип объектно-ориентированного программирования, к которому мы будем часто возвращаться.

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

PHP сам преобразовывает большинство элементарных значений в зависимости от контекста.

Например, если числа в строках используются в математических выражениях, то они преобразуются в эквиваленты целых значений или значений с плавающей точкой.

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

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

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

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

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


Уточнения типов объектов

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

Такая гибкость, с одной стороны, полезна, но, с другой, может стать причиной проблем при определении метода. Рассмотрим метод, предназначенный для работы с объектом типа ShopProduct.


class ShopProductWriter {
    public function write( $shopProduct ) {
        $str  = "{$shopProduct->title}: " .
                $shopProduct->getProducer() .
                " ({$shopProduct->price})\n";
        print $str;
    }
}

Мы можем протестировать этот класс следующим образом.


$product1 = new ShopProduct( "Собачье сердце", "Михаил", "Булгаков", 5.99 );
$writer = new ShopProductWriter();
$writer->write( $product1 );

Тогда на выходе получим следующее.


Собачье сердце: Михаил Булгаков (5.99)

Класс ShopProductWriter содержит единственный метод, write(). Методу write() передается объект типа ShopProduct. В нем используются свойства и методы последнего для построения и вывода результирующей строки описания товара.

Мы используем имя переменной-аргумента, $shopProduct, как напоминание напоминание программисту о том, что методу write() нужно передать объект типа shopProduct. Но это требование не является обязательным.

Это значит, что я могу передать некорректный объект или элементарный тип методу $write() и ничего об этом не узнать до момента обращения к аргументу $shopProduct.

К тому времени в нашем коде уже могут быть выполнены какие-либо действия так, как если бы мы передали методу настоящий объект типа ShopProduct.

*На заметку
Возможно, вас заинтересует, почему мы не добавили метод write() непосредственно в класс shopProduct. Все дело в ответственности. Класс shopProduct ответственен за хранение данных о товаре, а класс shopProductWriter - за вывод этих данных. По мере дальнейшего чтения (за пределами этой статьи) вы начнете понимать, в чем польза такого разделения ответственности.

Для решения описанной проблемы существует такая возможность, как уточнение типов данных класса. Чтобы добавить уточнение типа к аргументу метода, просто поместите перед ним имя класса. Поэтому метод write() можно изменить следующим образом.


public function write( ShopProduct $shopProduct ) {
    // ...
}

Теперь методу write() можно передавать аргумент $shopProduct, содержащий только объект типа shopProduct. Давайте попробуем вызвать метод write() для такого "хитрого" объекта.


class Wrong { }
$writer = new ShopProductWriter();
$writer->write ( new Wrong() );

Поскольку метод write() содержит уточнение типа класса, передача ему объекта Wrong приведет к неустранимой ошибке.


Catchable fatal error: argument 1 passed to ShopProductWriter::write() must be an instance of shopProduct, instance of Wrong given, ...

 

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

Он сразу же увидит требования метода write(). Программисту не нужно будет волноваться по поводу незаметных ошибок, возникающих в результате несоответствия типов аргументов, поскольку благодаря уточнению они определяются принудительно и строго.

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

Это значит, что уточнение класса сообщит об ошибке только тогда, когда нежелательный объект будет передан методу. И если вызов метода write() находится где-то глубоко в условном операторе, который запускается только в Новый Год, то, если вы тщательно не проверили код, вам придется работать на праздники.

Уточнения нельзя использовать для принудительного определения аргументов элементарных типов, таких как строки и целые значения. Для этой цели в теле методов следует использовать функции проверки типов, такие как is_int(). Но можно принудительно определить, что аргумент является массивом.


function setArray ( array $storearray )  {
	$this->array = $storearray;
}

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


function setWriter( ObjectWriter $objwriter=null ) {
    $this->writer = $objwriter;
}

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

Механизм, посредством которого различные классы можно группировать под одним типом, называется наследованием. О наследовании мы и поговорим в следующей статье "Наследование в PHP".

P.S. Вы определенно интересуетесь PHP и ООП:) Обратите внимание на премиум-уроки по различным аспектам сайтостроения, включая программирование на PHP, а также на бесплатный курс по созданию своей CMS-системы на PHP с нуля с использованием ООП:

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Смотрите также:

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

CodeIgniter: жив или мертв?

CodeIgniter: жив или мертв?

Функции обратного вызова, анонимные функции и механизм замыканий

Функции обратного вызова, анонимные функции и механизм замыканий

Применение функции к каждому элементу массива

Применение функции к каждому элементу массива

Слияние массивов. Преобразование массива в строку

Слияние массивов. Преобразование массива в строку

Деструктор и копирование объектов с помощью метода __clone()

Деструктор и копирование объектов с помощью метода __clone()

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

PHP: Удаление элементов массива

PHP: Удаление элементов массива

Ключевое слово final (завершенные классы и методы в PHP)

Ключевое слово final (завершенные классы и методы в PHP)

50 классных сервисов, программ и сайтов для веб-разработчиков

50 классных сервисов, программ и сайтов для веб-разработчиков

Наверх