PHP

Введение в ООП

Немного лирики

Хочу сразу предупредить - всё, что здесь написано, имеет статус ИМХО и ни в коем случае не является каким то постулатом.
Просто мои наблюдения и некоторые логические выкладки.

Так что же это такое - ООП и с чем его едят

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

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

Что мы собственно и наблюдаем последнее время. Программы неоправданно усложняются, растут в объёме и стоимости. Даже термин придумали - индусский код. То есть чем больше и сложнее программа, тем больше она стоит. А заказчик откуда может знать, что тоже самое можно сделать тремя строчками.

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

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

Самый большой затык - область видимости. У PHP одна глобальная область видимости, и если на одном конце программы объявлена переменная или функция, а на другом сделать тоже самое - получится конфликт. Это может нарушить логику программы. Особенно это сильно заметно, если разработчиков несколько.

Но. Эта опасность подстерегает девелоперов, если они пишут единую, цельную программу на несколько метров, а то и гектаров)))
А веб-приложение в идеале это не одна цельная программа, а много маленьких, вызываемых в нужное время. Откуда же появилась мода писать монстров?
Это все от лени и жажды наживы желания сделать программирование еще проще (казалось бы - куда еще то). То есть упростить существующий язык. И началось повальное изобретение фреймворков.

А что в итоге? Фреймворк, это по сути своей такой же язык программирования, который теперь выступает посредником между разработчиком и PHP. А значит он обязан быть цельным и неделимым, раз это язык. То есть мы должны иметь возможность в любое время вызвать любую команду, которую способен выполнить фреймворк.
И вот тут как раз и появляется первая грабля: область видимости. Что бы создать большую программу, нужно быть уверенным, что ничего ни где не конфликтует. Лучше всего поделить её на самостоятельные блоки. Но тогда пропадает возможность сделать сам фреймворк - он же должен быть неделимым.

Выход был найден - старое доброе Объектное Программирование. Разделяй программу на классы, организовывай нужные взаимодействия и все в шоколаде - ни кто никому не мешает. Однако за что боролись, на то и напоролись. Хотели облегчить жизнь - на самом деле жутко все усложнили. Теперь требуется изучать уже не просто язык программирования, а еще и отдельное направление в нем, а потом и язык фреймворка. Но изучить - пол беды, дело наживное. Главный подвох в управляемости и прозрачности кода. А тут полный абзац. Да и экономичным такой подход не назвать - ZEND фреймворк к примеру весит больше 5 метров и грузится практически весь. Даже если нужно всего навсего написать "Привет, Мир!"

Одно дело - прикладное программирование. Вы запускаете программу (тот же допустим редактор), она загружается в оперативку и работает, пока не будет нажата кнопка "Выход". Это называется - процесс. Тут ясен перец - нужно грузить все целиком, что бы не ждать подгрузки разных частей в процессе исполнения.
Другое дело веб. Программа (скрипт) работает короткое время, только чтобы сформировать страницу. Но таких процессов очень много - по числу пользователей. И чем больше программа, тем больше нужно оперативки для работы сайта. Попробуйте открыть одновременно 100 редакторов в своем компьютере... И если каждый занимает 300 mB, то оперативки потребуется 30000 mB. что не каждый компьютер потянет.

На мой взгляд ООП вообще не место в веб-программировании. Я имею ввиду парадигму, а не сами классы. Классы - вещь крайне удобная и полезная. А вот построение логики объектов, да еще и с прицелом на всеобъемлимость - горе от ума.
Особенно в части фреймворков. Вот представьте задачу - изготовить мобильный телефон с встроенной камерой. Если это реализовать с помощью фреймворка, то выйдет примерно это:



Мало того, что огромно, так еще и сложно в применении. Но зато все может...

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

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

Я все таки склонен делить функционал и не гнаться за унификацией. Это прозрачнее и экономичнее.

Если Вы успели изучить структуру нашего сайта в прошлых разделах, то могли бы заметить, что там идет именно такое разделение. Жесткое, физическое разделение, а не переопределение методов. И далее, при изучении основ ООП мы будем отталкиваться от этого, не доводя приложение до маразма абстракций. В вебе они хороши только в теории, на практике несут путаницу и разброд.

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

Так вот, лирика заканчивается. Начинается эта самая практика. А лирика была нужна для того, что бы Вы, прежде чем что то проектировать на ООП крепко задумались - к чему это может привести.
ООП - инструмент очень мощный и в неумелых руках может вызвать эфект, сопоставимый с вселенским потопом.

Самое начало

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

Но те, кто не останавливается на достигнутом, рано или поздно встречаются с таким синтаксисом, который не описан в пособиях для начинающих. И впадают в ступор. Что это за стрелочки, что за непонятные $this-> и так далее.

На самом деле это совсем не страшно, Достаточно понять один раз.

Попытаемся. Начнем с самого нуля.

И так, вот это простой, процедурный код:

<?php

    echo 'Привет, Мир!';


Вот это чуть посложнее - функция. Нам уже известная.

<?php

    function helloWorld() 
    { 
        return 'Привет, Мир!';     
    } 
     
    echo helloWorld();


Результат один и тот же, просто другая реализация.

А вот и то, чем пугают маленьких детей - класс.

<?php

    class My_First_Class 
    { 
        function helloWorld() 
        { 
            return 'Привет, Мир!';     
        } 
    } 
     
     
    $obj = new My_First_Class();
     
    echo $obj -> helloWorld();



Прослеживаете эволюцию? Сначала команду поместили в одну обертку - функцию, потом функцию поместили в класс.
Функция в классе называется метод. На примере не трудно разглядеть, как этот метод вызывается.

Но по порядку. Для того, что бы отправить строку в поток, мы использовали конструкцию echo. Это понятно.
Для того, что бы вернуть строку из функции, используется return и прямой вызов функции. Тоже ничего особенного.

А вот дальше появляются непонятные стрелочки. Дело в том, что класс, это как бы отдельная программа, которую можно записать в переменную. Называется объект.
То есть мы что сделали. Сначала в переменную $obj поместили эту программу (экземпляр класса), затем нам нужно из этого экземплра вызвать функцию (метод). Вот это и делается стрелочкой.
Вот так вызывается функция из глобальной области видимости:

echo helloWorld();


а так из объекта, который мы поместили в переменную $obj:

    $obj = new My_first_class();
      
    echo $obj -> helloWorld();


Теперь дальше. Для чего было нужно совать функцию в класс? Не ради красивого словца же. (Хотя встречаются и такие, кто любую, самую простенькую штатную конструкцию типа echo, одевают в три фуфайки, дабы код выглядел особо крутым, как первый парень на деревне).

Дело в том, что в классе может находиться несколько функций (методов). Для наглядности сделаем второй метод:

<?php

    class My_first_class 
    { 
        function helloWorld() 
        { 
            return 'Привет, Мир!';     
        } 
         
        function goodBye() 
        { 
            return 'Всем пока.';     
        }                 
    } 
     
     
    $obj = new My_first_class();
     
    echo $obj -> helloWorld(), '<br>'; 
    echo $obj -> goodBye();


Эти методы:
a) могут взаимодействовать между собой
б) им доступны общие переменные, которые видны только внутри класса и не видны снаружи.

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

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

<?php

    class My_first_class 
    { 
        function helloWorld($name) 
        { 
            return 'Привет, '. $name .'!';     
        } 
         
        function goodBye($name) 
        { 
            return 'Будь здоров, '. $name .'!';     
        }                 
    } 
     

    $name = 'Денис'; 
     
    $obj = new My_first_class();
     
    echo $obj -> helloWorld($name), '<br>'; 
    echo $obj -> goodBye($name);


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

<?php

    class My_first_class 
    { 
        function helloWorld($name) 
        { 
            $this->name = $name; 
            return 'Привет, '. $name .'!';     
        } 
         
        function goodBye() 
        { 
            return 'Будь здоров, '. $this->name .'!';     
        }                 
    } 
     

    $name = 'Денис'; 
     
    $obj = new My_first_class();
     
    echo $obj -> helloWorld($name), '<br>'; 
    echo $obj -> goodBye();


Вот она и попалась, эта непонятная $this->
Фокус в том, что внутри класса переменная объявляется не просто знаком доллара, а этой конструкцией. Всего то навсего.
То есть если внутри метода переменная обычная ($name), то она, как и в простой функции, видна только внутри метода. А если ей приляпать это: ($this->name), то её будет видно во всех методах, но не за пределами класса.
Важно помнить - это совершенно разные переменные, хоть и одинаково называются.

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

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

Как быть? А очень просто. Можно передать значение прямо в сам класс. Если сделать метод с таким же как класс названием:

<?php

    class My_first_class 
    { 
        function My_first_class($name) 
        { 
            $this->name = $name;    
        }         
         
        function helloWorld() 
        { 
            return 'Привет, '. $this->name .'!';     
        } 
         
        function goodBye() 
        { 
            return 'Будь здоров, '. $this->name .'!';     
        }                 
    } 
     

    $name = 'Денис'; 
     
    $obj = new My_first_class($name); 
         
    echo $obj -> helloWorld(), '<br>'; 
    echo $obj -> goodBye();


Вообще так было в 4-й версии PHP, хотя в пятой тоже работает. Но в пятой, более продвинутой, придумали специальный метод, который обязательно вызывается при инициализации объекта. Называется "конcтруктор" (__construct()). Делается так:

<?php

    class My_first_class 
    { 
        function __construct($name) 
        { 
            $this->name = $name;    
        }         
         
        function helloWorld() 
        { 
            return 'Привет, '. $this->name .'!';     
        } 
         
        function goodBye() 
        { 
            return 'Будь здоров, '. $this->name .'!';     
        }                 
    } 
     

    $name = 'Денис'; 
     
    $obj = new My_first_class($name); 
         
    echo $obj -> helloWorld(), '<br>'; 
    echo $obj -> goodBye();


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

    $obj = new My_first_class($name);


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

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

Наберитесь терпения.

Пространство имен

Ну с мертвой точки я думаю сдвинулись, продолжим. Одним из наиболее уместных на мой взгляд способов применения классов, является пространство имен. Что опять за непонятные термины? Это всего навсего расширенное понятие функции. Вспомните про жирафа, очень удобно написать функцию один раз, а потом пользоваться ей в разных местах. Так же и класс, используемый как отдельная программа, достаточно удобен. Отличие от функции очевидно - в классе может быть несколько функций. И они могут взимодействовать между собой.

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

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

<?php

    class My_first_class  
    {  
         
        var $hello   = array( 
                               'Привет', 
                               'Салют', 
                               'Хай',  
                               'Улыбнись',
                               'Рады видеть тебя'     
                             ); 
         

                                      
        function __construct($name)  
        {  
            $this->name = $name;     
        } 
         
       
          
        function helloUser()  
        {  
            return $this->hello[$this->random($this->hello)] .', '. $this->name .'!'; 
        }  
                 
         
        function random($arr) 
        { 
            return  rand(0, count($arr) - 1); 
        }                   
    }  
      

    $name = 'Денис';  
      
    $obj = new My_first_class($name);  
          
    echo $obj -> helloUser(), '<br>'; 


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

Но самое главное в другом. У нас появился метод random() Этот метод выдает случайное число в диапазоне от 0 до количества элементов массива минус один. То есть позволяет выбрать случайное приветствие. Это все хорошо, но суть не в том. Суть в том, что сочиняя такую функцию, первое имя для неё, которое приходит на ум - именно random() Так вот, если разработчиков несколько, то и функций с таким названием может оказаться несколько. Это внесет путаницу, а еще хуже - может вызвать конфликт. А при таком раскладе волноваться не о чем. Если нам нужна эта функция (метод), мы сначала определяем принадлежность, потом вызываем из нужного класса.

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

<?php

    class My_first_class  
    {  
         
        static $hello   = array( 
                               'Привет', 
                               'Салют', 
                               'Хай',                                
                               'Улыбнись',                                
                               'Рады видеть тебя'                 
                             ); 
       
                                              
         
        static function random($arr) 
        { 
            return rand(0, count($arr) - 1); 
        }                   
    }  
      

    $name = 'Денис';  
   
   $hello = My_first_class::$hello; 
   $rand = My_first_class::random($hello); 
    
    echo $helo[$rand] .', '. $name .'!';


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

Однако по порядку, что же тут изменилось. Во первых, переменные и метод в классе объявлены статичными. То есть к этим переменным теперь можно обращаться извне класса, определив только принадлежность. Делается это двойным двоеточием. Сначала пишем название класса, откуда нужно дернуть переменную, потом через двойное двоеточие саму переменную. Точно так же и с методом.

Но этод код тоже можно упростить. Дело в том, что совсем не обязательно передавать массив параметром в метод, он уже и так есть внутри класса. Так вот обратиться к статичной переменной внутри класса можно ключевым словом self, что ознаяает "местная":

<?php

    class My_first_class  
    {  
         
        static $hello   = array( 
                               'Привет', 
                               'Салют', 
                               'Хай',                                
                               'Улыбнись',                                
                               'Рады видеть тебя'                 
                             );
                                              
         
        static function random() 
        { 
            return rand(0, count(self::$hello) - 1); 
        }                   
    }  
      

    $name = 'Денис';  
   
   $hello = My_first_class::$hello; 
   $rand = My_first_class::random(); 
    
    echo $hello[$rand] .', '. $name .'!';


Можно пойти еще дальше, обратившись к методу точно так же как к переменной, посредством self

<?php

    class My_first_class  
    {  
         
        static $hello   = array( 
                               'Привет', 
                               'Салют', 
                               'Хай',                                
                               'Улыбнись',                                
                               'Рады видеть тебя'                 
                             );
                                              
        static function helloUser() 
        { 
            return self::$hello[self::random()]; 
        }           
                 
        static function random() 
        { 
            return rand(0, count(self::$hello) - 1); 
        }                   
    }  
      

    $name = 'Денис';  
   
   $hello = My_first_class::helloUser(); 
    
    echo $hello .', '. $name .'!';


Все просто и красиво.

Тут нужно подробнее остановиться на названиях классов и их местоположении. Что бы было легко ориентироваться в библиотеках, придумано так называемое "соглашение PEAR"
Следуя этим рекомендациям каждый класс нужно располагать в своем файле, который должен называться так же как класс. Еще в названии класса должен быть виден путь до файла, если класс в библиотеке не один. Допустим мы делаем библиотеку для работы с изображениями, в который входят классы ресайза, водных знаков, генерации графиков и т.д. Все они должны находиться в своем каталоге, к примеру pictures. Тогда класс ресайзера будет называться Pictures_Resizer, а водных знаков - Pictures_Watermark

Из названия класса видно, где его искать.

Ну а мы пойдем дальше, изучать инкапсуляцию.

Инкапсуляция.

Как и подавляющее большинство терминов в программировании, инкапсуляция вызывает смятение и панику.
Слово такое заумное, объяснение еще круче:
Инкапсуляция — свойство языка программирования, позволяющее объединить и защитить данные и код
в объект и скрыть реализацию объекта от пользователя (прикладного программиста).
При этом пользователю предоставляется только спецификация (интерфейс) объекта.
Википедия ©

Какие данные, от кого защищиать, что за секреты такие. Что за интерфейс? Где у него кнопка?

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



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

Рассмотрим для примера простенький скрипт грабителя грабера погоды.

Допустим мы решили у себя на сайте сделать такой информер:



Всё банально просто, берем код на Гисметео и вставляем к себе на сайт.

<a href="http://www.gismeteo.ru/city/daily/4064/"> 
<img src="http://informer.gismeteo.ru/new/4064-30.GIF" alt="GISMETEO: Погода по г.Бобруйск" title="GISMETEO: Погода по г.Бобруйск" border="0"> 
</a>


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

Так вот, к примеру нам нужен скрипт, который бы раз в сутки получал картинку с Гисметео и сохранял на нашем сервере. Это называется кэширование.
Это не сложно:

<?php

$url = 'http://informer.gismeteo.ru/new/4064-30.GIF'; 
$pic = 'cache/bobruisk.gif'; 
$time = 24; 

    if(!file_exists($pic) || date('U') - $time * 3600 > filemtime($pic)) 
    { 
        $info = file_get_contents($url); 
        file_put_contents($pic, $info);     
    } 
?> 
<img src="<?php echo $pic ?>"  border="0" />


Что тут. Мы смотрим, есть ли у нас в кэше нужный файл. А так же смотрим время его последнего изменения. Если оно больше суток - бежим в Гисметео и тырим свежую картинку.

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

<?php

    $url   = 'http://informer.gismeteo.ru/new/'; 
    $pic1  = '4064-30.GIF'; 
    $pic2  = '5045-30.GIF'; 
    $cache = 'cache/'; 
    $time  = 24;

/* 
* Function of cache the informant of weather 
* Функция кэширования информера погоды 
* @param string 
* @param string 
* @param string 
* @param integr 
* @return string 
*/ 
    function gisGrab($pic, $url, $caсhe, $time) 
    { 
        $flag = true; 
         
        if(!file_exists($cache . $pic) || date('U') - $time * 3600 > filemtime($cache . $pic)) 
        { 
            $flag = false; 
             
            if($info = @file_get_contents($url . $pic)) 
                if(file_put_contents($cache . $pic, $info))     
                    return $cache . $pic; 
        } 
                     
        return ($flag)?$cache . $pic : $url . $pic; 

    } 
?> 
<img src="<?php echo gisGrab($pic1, $url, $cache, $time) ?>"  border="0" /> 
<img src="<?php echo gisGrab($pic2, $url, $cache, $time) ?>"  border="0" />


Довольно надежная и фполне так себе функция.

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

<?php

    $url   = 'http://informer.gismeteo.ru/new/'; 
    $pic1  = '4064-30.GIF'; 
    $pic2  = '5045-30.GIF'; 
    $cache = 'cache/'; 
    $time  = 24; 

/* 
* Function of cache the informant of weather 
* Функция кэширования информера погоды 
* @param string 
* @param string 
* @param string 
* @param integr 
* @param string 
* @return string 
*/ 
    function gisGrab($pic, $url, $cache, $time, $last) 
    { 
        $flag = true; 
        static $trig = false; 
                 
        if(!$trig) 
        {    
            if(file_exists($cache . $pic) && date('U') - $time * 3600 < filemtime($cache . $last)) 
                $trig = true; 
                 
            if(!$trig && $info = @file_get_contents($url . $pic)) 
            { 
                $flag = false; 
                 
                if(file_put_contents($cache . $pic, $info))     
                    return $cache . $pic; 
            }                    
        } 
         
        return ($flag)?$cache . $pic : $url . $pic; 
    } 
?> 
<img src="<?php echo gisGrab($pic1, $url, $cache, $time, $pic2) ?>"  border="0" /> 
<img src="<?php echo gisGrab($pic2, $url, $cache, $time, $pic2) ?>"  border="0" />


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

Вот тут очень удобно использовать класс. Вот такой допустим:

<?php

/** 
 * IRB_Cache_Img - Class of preservation of foreign files 
 * NOTE: Requires PHP version 5 or later and GD version 2.0.1 or later 
 * @package irb_cache_img 
 * @author IT studio IRBIS-team 
 * @copyright © 2009 IRBIS-team 
 * @version 0.1 
 * @license http://www.opensource.org/licenses/rpl1.5.txt 
 */     

class IRB_Cache_Img 
{ 

    private  $url   = ''; 
    private  $cache = ''; 
    private  $time  = 0; 
    private  $flag  = true;     
     
/* 
* Constructor 
* @param string 
* @param string 
* @param integr 
*/         
    public function __construct($url, $cache = 'cache/', $time = 24) 
    { 
        $this->url  = $url; 
        $this->cache = $cache; 
        $this->time =  date('U') - $time * 3600;     
    } 
         
/* 
* The manager of pictures 
* @access public  
* @param string 
* @return string 
*/     
    public function getImg($pic) 
    { 
        if($this->flag) 
            $this->flag = $this->getTime($pic); 
             
        if($this->flag) 
            $pic = $this->writeCache($pic); 
        else 
            $pic = $this->cache . $pic; 
             
        return $pic;     
    } 
     
/* 
* Receives time of last change 
* @access private  
* @param string 
* @return boolean 
*/     
    private function getTime($pic)                 
    {     
        if(!file_exists($this->cache . $pic) || $this->time > filemtime($this->cache . $pic)) 
            return true; 
        else 
            return false; 
    }     
         
/* 
* Keeps a new picture 
* @access private  
* @param string 
* @return string 
*/     
    private function writeCache($pic)                 
    {     
        if(!$info = @file_get_contents($this->url . $pic)) 
            return $this->url . $pic; 
         
        if(!file_put_contents($this->cache . $pic, $info))     
            return $this->url . $pic;         

        return $this->cache . $pic; 
    }         

} 
/////////////////////////////////////////////////////////////// 
     
    $url = 'http://informer.gismeteo.ru/new/'; 
    $pic1 = '4064-30.GIF'; 
    $pic2 = '5045-30.GIF'; 

    $img = new IRB_Cache_Img($url); 
?> 


<img src="<?php echo $img -> getImg($pic2) ?>"  border="0" /> 
<img src="<?php echo $img -> getImg($pic1) ?>"  border="0"/>


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

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

Однако ближе к нашим барашкам. Во первых, как это работает. А во вторых, что за новые каракули: public и private?

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

Затем мы запускаем метод getImg(). Этот метод смотрит состояние флага. А так как мы ничегошеньки пока не успели натворить, то флаг равен true. А раз так все тоскливо, то вызываем другой метод, который определяет наличие файла и время его последней модификации (изменения).

Вроде бы все как в функции, так в чем тут подвох? А в том, что переменная, объявленная в экземпляре класса, живет до его скоропостижной кончины. Живут они долго и счастливо и помирают в один день. А значит флаг, поднятый назначенный при первом заходе, будет гордо реять, пока мы не получим все картинки. А так же в том, что мы можем на основании этого флага выбирать действия внутри класса - запускать метод кэширования или нет.

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

То есть порядок следования картинок теперь не важен. Смотрим время модификации первого попавшегося файла, а дальше ориентируемся по внутриклассовому флагу, который действителен на протяжении всей работы объекта. Сколько раз бы мы не вызывали метод getImg(). Именно первого файла, а не последнего, как в функции. Вот в чем вся тонкость и прелесть.

Обратите внимание, что методы внутри класса вызываются так же, как внутриклассовые переменные, а именно с $this->впереди().

Ну а теперь как раз и про объявление переменных. В 4-й версии PHP переменные объявлялись ключевым словом var. Это и в пятерке работает, но очень настоятельно не рекомендуется. А рекомендуется сразу установить доступы. Как переменным, так и методам. По умолчанию все методы публичны, то есть могут вызываться снаружи класса. Но лучше доступ задать явно.

Как можно догадаться, public обозначает - публичный. У нас метод такой тут один (не считая конструктора, он всегда публичный). А именно getImg(), который мы вызывали в сорце картинки. То есть снаружи класса. Остальные вне класса нам не нужны, вот их мы обозвали приватными, то есть внутренними (private). Их ни как не достать, они в домике. Есть еще третий вид - protected. С ним несколько сложнее, поймете, когда разберемся с наследованием.

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

/////////////////////////////////////////////////////////////// 
     
    $url = 'http://informer.gismeteo.ru/new/'; 
    $pic1 = '4064-30.GIF'; 
    $pic2 = '5045-30.GIF'; 

    $img = new IRB_Cache_Img($url); 
    $img -> writeCache($pic1); 
?> 


<img src="<?php echo $img -> getImg($pic2) ?>"  border="0" /> 
<img src="<?php echo $img -> getImg($pic1) ?>"  border="0"/>


и мы незамедлительно получим ошибку. Мол не суйся не в свое дело.

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

Потому что класс писан не нами, и что там творится - темный лес. Инкапсуляция. Черный ящик.
И начинаем мы медленно и верно жрать ресурс и ложить сервер. Мне на практике встречались такие сайты, которые после оптимизации снижали потребление ресурса в 30! раз. И все это из за неумелого обращения с инкапсуляцией. Это примерно как если бы мы заводили огромный КаМАЗ для того, что бы воспользоваться прикуривателем, хотя рядом лежит зажигалка.

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

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

Наследование

Ну здесь все проще, сам термин уже содержит объяснение этого явления

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

Так вот в программировании этот домик - базовый, родительский класс. Он будет неизменным, если конечно мы не спалим его, испив белого столового вина №21. А то, что мы имеем в данный момент, добавив к нему функционала - производные, дочерние классы.
Мы не строим каждый раз дом, что бы было где переночевать, мы просто добавляем к нему то, чего не хватает для приятного времяприпровождения

Рассмотрим это применительно к скриптам. Прямо как начали.
Вот домик:

<?php

/* 
* Class Small_house 
* @base 
*/   
class Small_house 
{ 
   static $house    = 'Домик'; 
   public $products = array( 
                             1 => 'Молоко', 
                                  'Сметана', 
                                  'Творог' 
                           );    

   public function goFazenda($eat) 
   { 
       $rest = self::$house .' + '.$this->tomilkCow($eat);  
       return $rest; 
   }  
    
   public function tomilkCow($eat) 
   { 
       return $this->products[$eat]; 
   }    
} 
/////////////////////////////////////////////////////
$rest = new Small_house();  
echo $rest -> goFazenda(1);


Можно спокойно поехать и попить молочка.

А можно прихватить детей и устроить им праздник:

<?php

/* 
* Class Small_house 
* @base 
*/   
class Small_house 
{ 
   static $house    = 'Домик'; 
   public $products = array( 
                             1 => 'Молоко', 
                                  'Сметана', 
                                  'Творог' 
                           );    

   public function goFazenda($eat) 
   { 
       $rest = self::$house .' + '.$this->tomilkCow($eat);  
       return $rest; 
   }  
    
   public function tomilkCow($eat) 
   { 
       return $this->products[$eat]; 
   }    
} 

/* 
* Class Kinder_garten 
* @extends Small_house 
*/ 
class Kinder_garten extends Small_house 
{ 

   public $toys = array( 
                             1 => 'Качеля', 
                                  'Лошадка', 
                                  'Басеин' 
                           ); 
                            
   public function restKinder($eat) 
   { 
       $rest = parent::goFazenda($eat) .' + '. implode(', ', $this->toys); 
       return $rest; 
   }      

} 
/////////////////////////////////////////////////////
$rest = new Kinder_garten(); 
echo $rest -> restKinder(2);


Вот заметьте, нет в новом классе никакой коровы, а сметана есть. А ведь мы не вызывали базовый класс. Почему так?
Потому что мы просто добавили к нашему пикнику игрушек, а корова и домик уже были в базовом классе. Нам совсем не обязательно каждый раз строить дом и разводить крупно-рогатый скот, раз у нас уже это все есть. Достаточно написать волшебное слово extends и имя класса, откуда мы хотим это все получить.
Обратиться к методу базового класса можно ключевым словом parent , то есть родительский.

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

Ну а раз дети резвятся, можно и немного нам расслабиться, если добавить к этим двум классам еще один:

<?php

/* 
* Class Crazy_party 
* @extends Kinder_garten 
*/ 
class Crazy_party extends Kinder_garten 
{ 

   public    $attribute   = ''; 
   public    $beef        = 'сырой шашлык'; 
   public    $beer        = 'пиво';  
   
   public function __construct($attribute = false) 
   { 
       $this->attribute = $attribute; 
   }    
                   
Теги:
ООП
Добавлено: 26 Февраля 2015 16:39:38 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...