Vamos dar inicio a mais uma série, onde vamos abordar alguns conceitos sobre os métodos mágicos do PHP. Apesar desta série não ser realmente relacionada à série sobre Padrões de Projetos eu confio que é de suma importância para que possamos continuar a melhorar nossos códigos e consequentemente nossos aplicativos.

Padrões melhoram seus códigos, sua manutenção, visão geral do programa, entre muitas outras vantagens que já foram citadas na publicação anterior; Os métodos mágicos na verdade são um complemento a muitas das vantagens que estes padrões de projeto podem oferecer.

Os métodos mágicos

O PHP nos oferece vários métodos mágicos que tendem a simplificar a maneira que desenvolvemos, alguns desses métodos acabam sendo mais utilizados, outros acabam caindo no esquecimento, e poucas vezes são lembrados.

  • __construct()
  • __destruct()
  • __set()
  • __get()
  • __call()
  • __toString()
  • __sleep()
  • __wakeup()
  • __isset()
  • __unset()
  • __autoload()
  • __clone()

Vou tentar abordar nesta série todos esses métodos, explicar seu funcionamento através de exemplos práticos que farão com que você compreenda o verdadeiro poder desses métodos e faça com que você passe a utiliza-los com mais frequência em seus projetos.

__set() e __get()

O __set() e __get() tem seu propósito explícito em seus nomes, e servem para facilitar a nossa vida, quando desejamos acessar propriedades privadas ou protegidas dentro de uma classe, sem a necessidade de implementar uma função para cada uma dessas propriedades.

__set() é executado ao se escrever dados para membros inacessíveis.

__get() é utilizados para ler dados de membros inacessíveis.

Vamos tentar colocar as coisas de uma forma mais visível; Observe o código abaixo:

<?php

class Human {

	private $name;
	private $age;

	public function __construct() {}

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

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

	public function setAge($age) {
		$this->age = $age;
	}

	public function getAge() {
		return $this->age;
	}

}

Repare no código acima, que possuímos duas propriedades privadas ($name e $age), obviamente estas não podem ser acessadas diretamente através da instância da classe, por este motivo criamos funções que alteram e resgatam seus valores.

Deixe-me apenas exemplificar a utilização da classe acima, supostamente contida no arquivo human.class.php:

<?php

@require_once 'human.class.php';

$human = new Human();
$human->setName('Ricardo');
$human->setAge(28);
echo 'This human named '.$this->getName().' has '.$this->getAge().' years old.';

Agora, vamos ver como ficaria o mesmo exemplo, caso utilizássemos os métodos mágicos __set() e __get().

<?php

class Human {

	private $name;
	private $age;

	public function __construct() {}

	public static function __set($name, $value) {
		$this->$name = $value;
	}

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

}

Agora nossa classe está usando os métodos mágicos, repare como conseguimos reduzir algumas linhas e já deixamos nosso código bem mais compreensível. Mas para entender melhor, vamos utilizar essa nova classe:

<?php

@require_once 'human.class.php';

$human = new Human();
$human::name = 'Ricardo';
$human::age = 28;
echo 'This human named '.$human::name.' has '.$human::age.' years old.';

Conseguiram perceber a melhora na legibilidade do nosso código? Mas até aí muita gente pode acabar se perguntando se esta é a única vantagem de utilizar esses métodos. Claro, que em projetos pequenos, a questão de desempenho é sempre menos evidente, porém quando trabalhamos com projetos de grande porte, algumas pessoas podem dizer que métodos mágicos deixam suas aplicações um pouco mais lentas. Isso é relativo, eu até hoje não achei nenhum benchmarking atual sobre o assunto, porém talvez mais confiável que encontrei foi escrito em 2007 por Larry Garfield e está disponível aqui, mas de lá pra cá muita coisa mudou. Tire você suas próprias conclusões.

Espero que tenham gostado, dúvidas/observações/correções deixem comentários.

Aviso!

Estou tendo pouco tempo para postar devido a inúmeros projetos que acabem surgindo no ultimo mês, os quais ainda estou desenvolvendo. Conforme tiver tempo volto a postar, de forma intercalada as publicações sobre Padrões de projetos e métodos mágicos do PHP.

  • Alair C
    Muito bom o post, parabéns, espero que continue postando.
    • http://www.luders.com.br Ricardo
      Muito obrigado, Alair.
      Está faltando um pouco de tempo para continuar as postagens, algumas só faltam revisar.
  • http://www.facebook.com/profile.php?id=100002493356671 Tiago Souza Ribeiro
    Ótimo post, aguardo pelos próximos sobre o assunto 😀
    Ah, uma coisa: os métodos _set() e _get() devem ser sempre estáticos como no seu código? E por que ao acessar as propriedades você as tratou como estáticas se lá na classe não tinha nenhum static?Obrigado, até mais.
    • http://www.luders.com.br/ Ricardo Lüders
      Tiago,

      Não há necessidade de ser estático, desde que seja público.

      Repare que mesmo sem eu definir os métodos __set e __get só é útil para definir o local de armazenamento das propriedades de um objeto e de alguma forma manipular os dados na hora de inserir ou recupera-los, pois por padrão é possível atribuir ou resgatar uma propriedade pública de um objeto sem a necessidade de declarar tais métodos.

      Tal como:

      nome = ‘Ricardo’;
      // Imprime: Meu nome é Ricardo!
      echo ‘Meu nome é ‘, $registrador->nome;

      Repare que no exemplo acima eu apenas atribuir a propriedade nome ao objeto e posteriormente resgatei o valor armazenado.

      Porém, se abaixo do mesmo código eu listar as propriedades e valores do objeto, você verá que ‘nome’ é uma propriedade do objeto.

      // Imprime: Array ( [nome] => Ricardo )
      echo print_r(get_object_vars($registrador), true);

      Faça alguns testes, atribua vários valores e utilize o comando acima para ver quantas propriedades seu objetos terá.

      Agora vamos fazer a mesma coisa porém usando métodos mágicos. Vamos melhorar nossa classe:

      class Registrador
      {
      protected $data = array();

      public function __set($i, $v)
      {
      $this->data[$i] = $v;
      }

      public function __get($i)
      {
      return $this->data[$i];
      }
      }

      Agora verifique como está a listagem de propriedades do seu objeto.
      Repare que o resultado da listagem será uma array vazia, sabe o motivo?

      É simples, altere o encapsulamento da array $data para public e tente novamente.
      Descobriu o motivo? Legal, né? Antes não era listado pois seus dados estavam ‘protegidos’ dentro do objetos e só poderiam ser lidos através de um método publico da própria classe.

      Bom, acho que é mais ou menos isso… 
      Faça alguns testes, não tenha medo de brincar, você sempre vai descobrir coisas novas sobre o PHP, afinal é uma linguagem repleta de truques e funções para fazer praticamente tudo.

      Espero ter ajudado.
      Abraços.

  • Julio Lira
    Ótimo tópico gostei ajudou bastante… eu procurei isto em tudo que é site e não encontrava, mas aqui eu encontrei (y)
  • Eduardo hillebrand
    Pode ser usada assim também:

    class Human {

    private $name;
    private $args = array();

    public function __construct() {}

    public static function __set($name, $value) {
    $this->args[$name] = $value;
    }

    public static function __get($name) {
    return $this->args[$name];
    }

    }

    assim você pode adicionar infinitos atributos atributos.

    $human->cabelo = “”;

    $human->olho = “”;

    $human->sexo= “”;

    $human->orientacaoSexual= “”;

    $human->tamanho= “”;

    $human->signo= “”;

    e por aí vai…

    • Eduardo hillebrand
      e pra resgatar é só usar echo $human->cabelo;
      • Ricardo Lüders
        Pode sim cara, sem problema algum. O importante é o conceito. Se você olhar nos comentários ali para baixo, eu já fiz este exemplo. 🙂