Программирование на C++

10 способов прострелить себе ногу

Создание веб приложений

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

Подпись для третьего не придумал

Американец изнасиловал лошадь, потому что думал, что у них родится кентавр

Помощь обездоленным якутам на дальнем севере

Благотворительность (на правах рекламы)

Показаны сообщения с ярлыком PHP. Показать все сообщения
Показаны сообщения с ярлыком PHP. Показать все сообщения

пятница, 20 декабря 2013 г.

Типаж (traits) в PHP



Новая фича, которая появилась с бетой PHP 5.4, называется Трейт (Типаж).
Трейт - единица повторного использования кода.

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

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

Совет для лучшего понимания и осмысления трейтов:
Это просто копипаст, то есть как будто в классе взяли и сделал include 'some.php' с каким то методом.

Еще информацию можно найти здесь:
http://habrahabr.ru/post/130000/ 
http://www.php.net/manual/ru/language.oop5.traits.php

Правила:

  • Трейт нельзя инстанцировать в какой-то объект (сначала надо добавить его в класс). 
  • В трейт можно добавлять и методы и переменные. 
  • Классы могут сколько угодно содержать трейтов. 
  • Трейт может содержать другие трейты 
  • Если разные классы будут использоват трейт со статическим свойством, то значение этого свойства будет разное. 
  • В отличие от обычного наследования. 
  • В типаже можно объявлять статические методы, но нельзя объявлять статические свойства. Но это можно обойти. 

1. Синтаксис

Трейт похож на класс, его объявление начинается со слова trait:

trait TSingleton {
  private static $_instance = null;

  public static function getInstance() {
    if (null === self::$_instance)
    {
      self::$_instance = new self();
    }

    return self::$_instance;
  }
}
class FrontController {
  use TSingleton;
}


// Также может быть использован в уже унаследованном классе
class WebSite extends SomeClass {
  use TSingleton;
}

2. Класс может также содержать неограниченное количество трейтов

(пример взят с википедии), трейты указываются через запятую:

// Шаблон
trait TBounding {
  public $x, $y, $width, $height;
}
trait TMoveable {
  public function moveTo($x, $y) {
    // ...
  }
}
trait TResizeable {
  public function resize($newWidth, $newHeight) {
    // ...
  }
}

// Несколько трейтов
class Rectangle {
  use TBounding, TMoveable, TResizeable; 
 
  function fillColor($color) {
    // ...
  }
}

3. Трейт может содержать другие трейты:

trait HelloWorld
{
    use Hello, World;
}
class MyWorld
{
    use HelloWorld;
}

4. Трейты и порядок выполнения.

Не стоит забывать, что:

1)Сначала:
Методы трейта переписывают унаследованные методы родительского класса
2)Потом уже:
Методы определенные в текущем классе переписывают методы из трейта

trait Hello
{
    function sayHello() {
        return "Hello";
    }
    function sayWorld() {
        return "Trait World";
    }
    function sayHelloWorld() {
        echo $this->sayHello() . " " . $this->sayWorld();
    }
    function sayBaseWorld() {
        echo $this->sayHello() . " " . parent::sayWorld();
    }
}
class Base
{
    function sayWorld(){
        return "Base World";
    }
}
class HelloWorld extends Base
{
    use Hello;
    function sayWorld() {
        return "World";
    }
}
$h =  new HelloWorld();
$h->sayHelloWorld(); // Hello World
$h->sayBaseWorld(); // Hello Base World

Объяснение:
Мы имеем результат Hello World, так как у нас в текущем классе (производном) определен метод, который ПЕРЕКРЫВАЕТ метод из трейта.
Поэтому,если мы хотим, чтобы HelloWorld объект выводил также Base World, нам необходимо использовать ключевое слово ::parent.

5. Конфликт имен

Может возникнуть ситуация, что два трейта содержат функцию с одинаковым именем, тогда PHP выкинет fatal error ошибку

trait Game
{
    function play() {
        echo "Playing a game";
    }
}
trait Music
{
    function play() {
        echo "Playing music";
    }
}
class Player
{
    use Game, Music;
}
$player = new Player();
$player->play();

Решение:
Нужно использовать ключевое слово instead:

class Player
{
  use Game, Music {
       Music::play insteadof Game;
  }
}
$player = new Player();
$player->play(); //Выведет: Playing music

Категория отличие от обычного наследования: 

6. Статические свойства в трейтах

Как мы помним, в наследовании, если наследуется класс со статическим свойством, то это статическое поле содержит одно и то же значение, в трейтах не так, класс в трейтах использует разные объекты;

Пример на родительских классах:

class TestClass {     
    public static $_bar; 
} 
class Foo1 extends TestClass { } 
class Foo2 extends TestClass { }
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World'; 
echo Foo1::$_bar . ' ' . Foo2::$_bar; // Выведет: World World
Пример с использованием трейта:

trait TestTrait {     
    public static $_bar; 
} 
class Foo1 {     
    use TestTrait; 
} 
class Foo2 {    
    use TestTrait; 
}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World'; 
echo Foo1::$_bar . ' ' . Foo2::$_bar; // Выведете: Hello World

7. Доступ из трейта к свойствам наследуемого класса:

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

trait MyTrait{   
    protected function accessVar()   {     
       return $this->var;   
    } 
} 
class TraitUser{   
    use MyTrait;   
    private $var = 'var';   
    public function getVar()   {     
         return $this->accessVar();   
    } 
} 

$t = new TraitUser(); 
echo $t->getVar(); // -> 'var'  

четверг, 19 декабря 2013 г.

Разница между $this и self в PHP. Подробная версия.


У каждого обычно возникает вопрос, что такое $this, что такое self, для чего они используются и в чем разница между ними?
ПРАВИЛА КОТОРЫЕ ВЫ ДОЛЖНЫ ЗАПОМНИТЬ:

  1. Статические функции должны использовать только статические переменные.
  2. self ТОЛЬКО для статических функций, свойств. Но также можно вызвать нестатический метод, КАК СТАТИЧЕСКИЙ через self. Но лучше так не делать, а то папа побьет.
  3. this ТОЛЬКО для нестатических.
  4. this требует, чтобы класс был проинстанцирован, self не требует.

$this - это ССЫЛКА на ТЕКУЩИЙ объект и она нужна, чтобы обратиться к переменной в КОНТЕКСТЕ класса. К примеру:

class human {
     public $name;
     public function klassFunc() {};
     function getName() {
            echo $name; // неправильно, обратиться не к переменной public name
            echo $this->name; // правильно
            
            //вызов метода класса
            $this->klassFunc();
     }
}

Обычна нужна, чтобы инициализировать поля в конструкторе, ну и не только:

class human {
     public $name;   
     public function __construct($name){
          $this->name = $name;
          $name = $name; //будет ошибка
     }
}

$self используется в том, же самом ключе, но уже для СТАТИЧЕСКИХ свойств:

class human {
     static $name = "ololo";
     
     public function klassFunc() {};
     function getName() {
            echo self::$name; //обращение к name
            echo $this->name; //null или ошибка. НЕЛЬЗЯ
     }
}

Проверочная программа которая показывает главную разницу между self и $this:

class Klass {
const       
STAT 'S' // константы всегда статичныstatic     $stat 'Static' ;
public     
$publ 'Public' ;
private    
$priv 'Private' ;
protected  
$prot 'Protected' ;

function 
__construct( ){  }

public function 
show( ){
    print 
'<br> self::STAT: '.self::STAT// ссылается на константу
    
print '<br> self::$stat: '.self::$stat// статическая переменная.
    
print '<br>$this->stat: '.$this->stat // Ошибки нет, но выведет пустую строку. ПРЕДУПРЕЖДЕНИЕ.
    
print '<br>$this->publ: '.$this->publ // выведет как надо
    
print '<br>' ;
}
}
$me = new Klass() ;$me->show() ;

Результат:
self::STAT: S
self::$stat: Static
Strict Standards: Accessing static property Klass::$stat as non static in htdocs\test\testReference.php on line ..
Notice: Undefined property: Klass::$stat in C:\xampp\htdocs\test\testReference.php on line 50
$this->stat:
$this->publ: Public
Видно, что php умный и кидает предупреждения глупым программистам. Не делайте так.

Второе главное отличие состоит в том, что

self не использует таблицу виртуальных методов, то есть:
(взято с StackOverflow)

class Person {
    private $name;

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

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();
Выведет:

Hello, I'm Ludwig the geek
Goodbye from Ludwig the person

Пояснение:
Метод sayHello использует this, поэтому виртуальная таблица вызовет Geek::getTitle()
Метод sayGoodbye использует self, поэтому виртуальная таблица будет не задействована и будет вызван parent::getTitle()

Решение:
Используем вместо self, static keyword:
public function sayGoodbye() {
      echo "Goodbye from ".static::getTitle();

Всем, спасибо, за внимание :)