5. Os objetos
5.1. Qualquer variável pode tornar-se um objeto dotado de atributos (exemple_14)
<?php
// qualquer variável pode ter atributos por definição
$obj1->attr1 = "un";
$obj1->attr2 = 100;
// exibe o objeto
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// altera o objeto
$obj1->attr2+=100;
// exibe o objeto
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// atribui o valor do objeto1 ao objeto2
$obj2 = $obj1;
// altera o objeto 2
$obj2->attr2 = 0;
// exibe os dois objetos
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet2=[$obj2->attr1,$obj2->attr2]\n";
// atribui a referência do objeto1 ao objeto3
$obj3 = &$obj1;
// altera o objeto 3
$obj3->attr2 = 10;
// exibe os dois objetos
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet3=[$obj3->attr1,$obj3->attr2]\n";
// um objeto é um dicionário?
print count($obj1)."\n";
while (list($attribut, $valeur) = each($obj1))
print "obj1[$attribut]=$valeur\n";
// fim
exit;
?>
Resultados:
objet1=[un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[un,10]
objet3=[un,10]
1
obj1[attr1]=un
obj1[attr2]=10
Comentários
- linha 4: a notação $obj->attr designa o atributo attr da variável $obj. Se não existir, é criado, transformando assim a variável $obj num objeto com atributos.
- linha 13: a expressão $obj2=$obj1, quando $obj1 é um objeto, constitui uma cópia de objetos por referência. Assim, $obj2 e $obj1 são referências ao mesmo objeto. O próprio objeto pode ser alterado por qualquer uma das referências.
- linha 20: a expressão $obj3=&$obj1 atribui a $obj3 a referência (o endereço) da variável $obj1. Se $obj1 for um objeto, como é o caso aqui, a expressão anterior é equivalente a $obj3=$obj1.
- linhas 28-29: mostram que um objeto pode ser percorrido como um dicionário. As chaves do dicionário são os nomes dos atributos e os valores do dicionário são os valores desses mesmos atributos.
- linha 27: a função count pode ser aplicada a um objeto, mas não fornece, como seria de esperar, o número de atributos. Assim, um objeto apresenta semelhanças com um dicionário, mas não é um dicionário.
5.2. Uma classe Pessoa sem atributos declarados (exemple_15)
<?php
class Personne {
// atributos da classe
// não declarados — podem ser criados dinamicamente
// método
function identité() {
// à primeira vista, utiliza atributos inexistentes
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// teste
// os atributos são públicos e podem ser criados dinamicamente
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// chamada de um método
print "personne=" . $p->identité() . "\n";
// fim
exit;
?>
Resultados:
Comentários
- linhas 3-13: definem uma classe Personne. Uma classe é um modelo a partir do qual se criam objetos. Uma classe é um conjunto que agrupa atributos e funções chamadas métodos. Não é obrigatório declarar os atributos.
- linhas 9-12: o método identité apresenta o valor de três atributos não declarados na classe. A palavra-chave $this designa o objeto ao qual se aplica o método.
- linha 17: cria-se um objeto $p do tipo Personne. A palavra-chave new serve para criar um novo objeto. A operação devolve uma referência ao objeto criado (ou seja, um endereço). São possíveis várias formas de escrita: new Pessoa(), new Pessoa, new pessoa. O nome da classe não distingue maiúsculas de minúsculas.
- linhas 18-20: os três atributos necessários para o método identité são criados no objeto $p.
- linha 22: o método identité da classe Personne é aplicado ao objeto $p. No código (linhas 9-11) do método identité, $this faz referência ao mesmo objeto que $p.
5.3. A classe Pessoa com atributos declarados (exemple_16)
<?php
class Personne {
// atributos da classe
var $prénom;
var $nom;
var $âge;
// método
function identité() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// teste
// os atributos são públicos
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// chamada de um método
print "personne=" . $p->identité() . "\n";
// fim
exit;
?>
Resultados:
Comentários
- linhas 6-8: os atributos da classe são explicitamente declarados com a palavra-chave var.
5.4. A classe Pessoa com um construtor (exemple_17)
Os exemplos anteriores mostravam classes Personne pouco comuns, tal como as que se podiam encontrar em PHP 4. Não é aconselhável seguir estes exemplos. Apresentamos agora uma classe Personne que segue as boas práticas de PHP 5:
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
public function getNom() {
return $this->nom;
}
public function getAge() {
return $this->âge;
}
public function setPrénom($prénom) {
$this->prénom = $prénom;
}
public function setNom($nom) {
$this->nom = $nom;
}
public function setAge($age) {
$this->âge = $age;
}
// construtor
function __construct($prénom, $nom, $âge) {
// passamos pelos set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// teste
// criação de um objeto «pessoa»
$p = new Personne("Paul", "Langevin", 48);
// identidade dessa pessoa
print "personne=$p\n";
// alteração da idade
$p->setAge(14);
// identidade da pessoa
print "personne=$p\n";
// fim
exit;
?>
Resultados:
Comentários
- linhas 3-48: a classe Personne
- linhas 6-8: os atributos privados (private) da classe. Estes atributos só são visíveis no interior da classe. As outras palavras-chave que podem ser utilizadas são:
- public: torna o atributo público, visível a partir do exterior da classe
- protected: torna o atributo protegido, visível a partir do interior da classe e das classes dela derivadas.
- Como os atributos são privados, não é possível aceder-lhes a partir do exterior da classe. Por isso, não é possível escrever o seguinte código:
Aqui, estamos fora da classe Personne. Como o atributo nom é privado, a linha 2 está incorreta. Para inicializar os campos privados do objeto $p, existem duas formas:
- (continuação)
- utilizar os métodos públicos set e get (o nome destes métodos pode ser qualquer um) das linhas 11-33. Poderemos então escrever:
- (continuação)
- utilizar o construtor das linhas 36-41. Nesse caso, escrever-se-á:
A sintaxe acima chama automaticamente o método da classe Personne denominado __construct.
- linha 54: esta linha apresenta a pessoa $p sob a forma de uma cadeia de caracteres. Para tal, é utilizado o método da classe Personne, denominado __toString.
5.5. A classe Pessoa com controlo de validade no construtor (exemple_18)
O construtor de uma classe é o local adequado para verificar se os valores de inicialização do objeto estão corretos. No entanto, um objeto também pode ser inicializado através dos seus métodos set ou equivalentes. Para evitar colocar as mesmas verificações em dois locais diferentes, estas serão incluídas nos métodos set. Se um valor de inicialização de um objeto se revelar incorreto, será lançada uma exceção. Eis um exemplo:
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
public function getNom() {
return $this->nom;
}
public function getAge() {
return $this->âge;
}
public function setPrénom($prénom) {
// o nome próprio não pode estar vazio
if (!preg_match("/^\s*(.+?)\s*$/", $prénom, $champs)) {
throw new Exception("Le prénom doit être non vide");
} else {
$this->prénom = $champs[1];
}
}
public function setNom($nom) {
// o apelido não pode estar vazio
if (!preg_match("/^\s*(.+?)\s*$/", $nom, $champs)) {
throw new Exception("Le nom doit être non vide");
} else {
$this->nom = $champs[1];
}
}
public function setAge($âge) {
// a idade deve ser válida
if (!preg_match("/^\s*(\d+)\s*$/", $âge, $champs)) {
throw new Exception("L'âge doit être un entier positif ou nul");
} else {
$this->âge = $champs[1];
}
}
// construtor
function __construct($prénom, $nom, $âge) {
// utiliza-se os set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// teste
// criação de um objeto Pessoa
$p = new Personne("Paul", "Langevin", 48);
// identidade dessa pessoa
print "personne=$p\n";
// criação de um objeto Pessoa com erro
try {
$p = new Personne("xx", "yy", "zz");
} catch (Exception $e) {
print $e->getMessage();
}
// fim
exit;
?>
Resultados:
Comentários
- linhas 23-30: inicialização do atributo prénom e verificação do valor de inicialização
- linha 25: utilização de uma expressão regular. O nome próprio é uma sequência de um ou mais caracteres, eventualmente seguida ou precedida de espaços.
- linha 26: se o apelido estiver vazio, é lançada uma exceção; caso contrário, o nome próprio é guardado (linha 28)
- linha 66: utilização do construtor da classe Personne
- linha 68: utilização do método __toString da classe Personne
- linhas 70-74: tratamento da eventual exceção lançada pelo construtor da classe Personne.
5.6. Adição de um método que funciona como segundo construtor (exemple_19)
Em PHP 5, não é possível ter vários construtores com parâmetros diferentes que permitam construir um objeto de diversas formas. É, portanto, possível utilizar métodos que funcionam como construtores:
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
….
// construtor
function __construct($prénom, $nom, $âge) {
// utilizando os set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método
public function initWithPersonne($p) {
// inicializa o objeto atual com uma pessoa $p
$this->__construct($p->prénom, $p->nom, $p->âge);
}
// método toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// teste
// criação de um objeto Pessoa
$p = new Personne("Paul", "Langevin", 48);
// identidade dessa pessoa
print "personne=$p\n";
// criação de uma segunda pessoa
$p2 = new Personne("Laure", "Adeline", 67);
// inicialização da primeira com os valores da segunda
$p->initWithPersonne($p2);
// verificação
print "personne=$p\n";
// fim
exit;
?>
Resultados:
Comentários
- linhas 26-29: o método initWithPersonne permite atribuir ao objeto atual os valores dos atributos de outro objeto Personne. Aqui, recorre ao construtor __construct, mas isso não é obrigatório.
5.7. Uma matriz de objetos Pessoa (exemple_20)
O exemplo seguinte mostra que é possível ter tabelas de objetos.
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
...
// construtor
function __construct($prénom, $nom, $âge) {
// passamos pelos set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método
public function initWithPersonne($p) {
….
}
// método toString
function __toString() {
...
}
}
// teste
// criação de uma tabela de objetos Pessoa
$groupe = array(new Personne("Paul", "Langevin", 48), new Personne("Sylvie", "Lefur", 70));
// identidade dessas pessoas
for ($i = 0; $i < count($groupe); $i++)
print "groupe[$i]=$groupe[$i]\n";
// fim
exit;
?>
Resultados:
Comentários
- linha 39: criação de uma tabela com 2 pessoas
- linha 42: percurso pela tabela
- linha 43: $groupe[$i] é um objeto do tipo Personne. O método __toString é utilizado para o apresentar.
5.8. Criação de uma classe derivada da classe Pessoa (exemple_21)
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
...
// construtor
function __construct($prénom, $nom, $âge) {
// utiliza-se os set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método
public function initWithPersonne($p) {
// inicializa o objeto atual com uma pessoa $p
$this->__construct($p->prénom, $p->nom, $p->âge);
}
// método toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// uma classe derivada de Pessoa
class Enseignant extends Personne {
// atributos
private $discipline; // disciplina lecionada
// getter e setter
public function getDiscipline() {
return $this->discipline;
}
public function setDiscipline($discipline) {
$this->discipline = $discipline;
}
// construtor
public function __construct($prénom, $nom, $âge, $discipline) {
// atributos do pai
parent::__construct($prénom, $nom, $âge);
// outros atributos
$this->setDiscipline($discipline);
}
// sobrecarregamento da função __toString da classe pai
public function __toString() {
return "[" . parent::__toString() . ",$this->discipline]";
}
}
// teste
// criação de uma tabela de objetos «Pessoa» e derivados
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70));
// identidade dessas pessoas
for ($i = 0; $i < count($groupe); $i++)
print "groupe[$i]=$groupe[$i]\n";
// fim
exit;
?>
Resultados:
Comentários
- linhas 3-35: a classe Personne
- linha 38: a classe Enseignant deriva (extends) da classe Personne. A classe derivada Enseignant herda os atributos e os métodos da sua classe-mãe.
- linha 41: a classe Enseignant adiciona um novo atributo, discipline, que lhe é próprio
- linha 55: o construtor da classe Enseignant recebe 4 parâmetros
- 3 para inicializar a sua classe pai (nome próprio, apelido, idade)
- 1 para a sua própria inicialização (disciplina)
- linha 57: a classe derivada tem acesso aos métodos e construtores da sua classe pai através da palavra-chave
parent::. Aqui, passam-se os parâmetros (nome próprio, apelido, idade) ao construtor da classe pai. - linha 64: o método __toString da classe derivada utiliza o método __toString da classe pai.
- linha 71: um array que contém um objeto Enseignant e um objeto Personne.
- linha 74: é executado um ciclo sobre os elementos da matriz
- linha 75: o método __toString de cada elemento $groupe[$i] será chamado. A classe Personne possui um método __toString. A classe Enseignant tem dois: o da sua classe pai e o seu próprio. Podemos questionar-nos sobre qual deles será chamado. A execução mostra que foi o da classe Enseignant que foi chamado. É sempre assim: quando um método é chamado num objeto, procura-se esse método pela seguinte ordem: no próprio objeto, na sua classe pai, se houver, depois na classe pai da classe pai, etc. A pesquisa termina assim que o método for encontrado.
5.9. Criação de uma segunda classe derivada da classe Pessoa (exemple_22)
O exemplo seguinte cria uma classe Etudiant derivada da classe Personne:
<?php
class Personne {
// atributos da classe
private $prénom;
private $nom;
private $âge;
// getters e setters
public function getPrénom() {
return $this->prénom;
}
...
// construtor
function __construct($prénom, $nom, $âge) {
// utiliza-se os set
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// método
public function initWithPersonne($p) {
// inicializa o objeto atual com uma pessoa $p
...
}
// método toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
// uma classe derivada de «pessoa»
class Enseignant extends Personne {
// atributos
private $discipline; // disciplina lecionada
// getter e setter
...
// construtor
public function __construct($prénom, $nom, $âge, $discipline) {
// atributos do pai
parent::__construct($prénom, $nom, $âge);
// outros atributos
$this->setDiscipline($discipline);
}
// sobrecarregamento da função _identité__toString da classe pai
public function __toString() {
return "[" . parent::__toString() . ",$this->discipline]";
}
}
// uma classe derivada de Pessoa
class Etudiant extends Personne {
// atributos
private $formation; // disciplina lecionada
// getter e setter
public function getFormation() {
return $this->formation;
}
public function setFormation($formation) {
$this->formation = $formation;
}
// construtor
public function __construct($prénom, $nom, $âge, $formation) {
// atributos do pai
parent::__construct($prénom, $nom, $âge);
// outros atributos
$this->setFormation($formation);
}
// sobrecarregamento da função __toString da classe pai
public function __toString() {
return "[" . parent::__toString() . ",$this->formation]";
}
}
// teste
// criação de uma tabela de objetos «pessoa» e derivados
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70), new Etudiant("Steve", "Boer", 23, "iup2 qualité"));
// identidade dessas pessoas
for ($i = 0; $i < count($groupe); $i++)
print "groupe[$i]=$groupe[$i]\n";
// fim
exit;
?>
Resultados:
groupe[0]=[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
groupe[2]=[[Steve,Boer,23],iup2 qualité]
5.10. As interfaces (exemple_23)
Uma interface é uma estrutura que define protótipos de métodos. Uma classe que implementa uma interface define o código de todos os métodos da interface.
<?php
// uma interface
interface IExemple {
function ajouter($i, $j);
function soustraire($i, $j);
}
// implementação 1 da interface
class Classe1 implements IExemple {
public function ajouter($a, $b) {
return $a + $b + 10;
}
public function soustraire($a, $b) {
return $a - $b + 20;
}
}
// implementação 2 da interface
class Classe2 implements IExemple {
public function ajouter($a, $b) {
return $a + $b + 100;
}
public function soustraire($a, $b) {
return $a - $b + 200;
}
}
// função
function calculer($a, $b, IExemple $interface) {
print $interface->ajouter($a, $b)."\n";
print $interface->soustraire($a, $b)."\n";
}
// ------------------- main
// criação de 2 objetos do tipo Classe1 e Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// chamada à função «calcular»
calculer(4, 3, $c1);
calculer(14, 13, $c2);
?>
Resultados
Comentários
- linhas 4-8: a interface IExemple define dois métodos: a função ajouter (linha 5) e a função soustraire (linha 7). A interface não define o código destes métodos. São as classes que implementam a interface que o farão.
- linha 11: a classe Classe1 implementa a interface IExemple. Por conseguinte, define um código para os métodos ajouter (linha 13) e soustraire (linha 17).
- linhas 24-34: o mesmo se aplica à classe Classe2
- linha 37: o método calculer aceita três parâmetros: dois inteiros, $a e $b, e um objeto $interface que implementa a interface IExemple. Poderíamos ter escrito function calcular($a,$b,$interface) sem indicar o tipo IExemple do parâmetro $interface. Ao indicá-lo, permite-se que o interpretador PHP verifique se o parâmetro efetivo é, de facto, um objeto que implementa a interface IExemple.
- linhas 38-39: utilização dos métodos ajouter e soustraire do objeto $interface. Como este implementa a interface IExemple, sabemos que possui estes dois métodos.
- linhas 44-45: criação de dois objetos de tipos diferentes, mas que implementam ambos a interface IExemple.
- linha 47: passa-se à função calculer, o objeto $c1 do tipo Classe1 que implementa a interface IExemple.
- linha 48: passa-se para a função calculer, sendo que o objeto $c2, do tipo Classe2, implementa a interface IExemple.