Solucionado (ver solução)
Solucionado
(ver solução)
9
respostas

use namespace e classes

Pessoal, eu tentei resumir o meu problema para ficar mais facil de visualizar: https://drive.google.com/file/d/1eKwJxwSYdhEBehdXPFff1guqGHkHs5b-/view?usp=sharing

Criei 2 arquivos: 014_namespaces_01.php 015_namespaces_02.php

No primeiro, defini o namespace "xadai\oie" e criei a classe "Animal". No segundo, tento usar o namespace "xadai\oie" e extender a classe "Animal". Em seguida, tento usar o metodo "speak" atraves da classe "Person" que herda de "Animal". Porem, o erro aparece:

PHP Fatal error:  Class 'xadai\oie\Animal' not found in /home/xadai/Documents/xadai-it-studies/programming-languages/php/015_namespaces_02.php on line 5

Os arquivos estao na mesma pasta, e eu nao estou entendendo o que esta acontecendo. Help, please!

9 respostas

Oi Xadai,

Para o namespace funcionar, é necessário que tenha sido registrado um autoload para que o PHP saiba onde encontrar as classes.

Pelo print que você passou, nenhum dos arquivos está importando um autoload, por isso não conseguem encontrar a classe.

No capitulo 2 desse curso é explicado como tudo isso funciona.

Você já chegou nessa parte do curso?

Na aula "05: Utilizando os namespaces" o professor faz funcionar sem o autoload, que eh apresentado na aula subsequente "07: Autoload de classes" Eu cheguei nessa aula, mas ela tambem nao deu certo. Como a anterior nao deu certo, eu queria entende-la primeiro, para prosseguir. Aos 3:37 da aula 05 ele verifica se esta funcionando apenas com "require_once" e "use". E funciona, sem autoloader.

Certo, então vamos lá... Realmente me expressei mal. É possível utilizar namespaces sem autoload utilizando a técnica que você disse, usando require. Veja como seria o certo:

Arquivo 014_namespaces_01.php:

<?php

namespace xadai\oie;

class Animal
{

    public function speak(): void
    {
        echo 'I am an animal' . PHP_EOL;
    }

}

Arquivo 015_namespaces_02.php:

<?php

namespace xadai\oie;

class Person extends Animal
{

}

Arquivo teste-namespace.php:

<?php

use xadai\oie\Person;

require '014_namespaces_01.php';
require '015-namespaces_02.php';

$person = new Person();
$person->speak();

Eu criei um arquivo de testes separadamente apenas para ficar organizado e seguir boas práticas. Nele eu faço a inclusão dos dois arquivos de classe, após crio umas instância de Person e chamo seu método speak. Nesse caso como ambos os arquivos estão no mesmo diretório, deve funcionar corretamente.

Veja que na classe Person não preciso do use pois ambas classes estão no mesmo namespace, fazendo isso ser desnecessário.

Eu poderia fazer como no seu exemplo, criando a instância de Person no mesmo arquivo que eu declaro a classe, nesse caso ficando assim:

Arquivo 015_namespaces_02.php:

<?php

namespace xadai\oie;

require '014_namespaces_01.php';

class Person extends Animal
{

}

$person = new Person();
$person->speak();

Veja que eu estou fazendo o require do arquivo com a classe Animal. Com isso consigo corretamente fazer o extends dela.

O autoload é apenas um forma de facilitar a sua aplicação, pois ele será chamado sempre que uma classe for requisitada e não for de conhecimento do PHP onde ela está. Nesse caso o autoload é chamado para fazer a procura a partir de suas regras e fazer automaticamente o require da class.

Para o autoload funcionar, algumas coisas precisam ser alteradas...

Primeiro o nome do arquivo precisa ser o mesmo nome que o do classe, pois assim facilita o uso do autoload de forma mais genérica.

Depois, as suas classes precisam estarem todas dentro de um mesmo diretório base, visando identificar em qual diretório o autoload vai procurar.

Com isso, teríamos algo assim:

Arquivo src/Animal.php:

<?php

namespace xadai\oie;

class Animal
{

    public function speak(): void
    {
        echo 'I am an animal' . PHP_EOL;
    }

}

Arquivo src/Person.php:

<?php

namespace xadai\oie;

class Person extends Animal
{

}

Arquivo autoload.php:

<?php

spl_autoload_register(function (string $nomeCompletoDaClasse){
    $caminhoArquivo = str_replace('xadai\\oie', 'src', $nomeCompletoDaClasse);
    $caminhoArquivo = str_replace('\\', DIRECTORY_SEPARATOR, $caminhoArquivo);
    $caminhoArquivo .= '.php';

    if(file_exists($caminhoArquivo)) {
        require_once $caminhoArquivo;
    }
});

Arquivo teste-namespaces.php:

<?php

use xadai\oie\Person;

require 'autoload.php';

$person = new Person();
$person->speak();

Veja que os arquivos de classe Persone Animal ambos eu movi para a pasta src pois é nela que o autoload começara a procurar.

E no arquivo de teste testes-namespaces.php, que é o que irei executar, eu fiz a inclusão do autoload e a partir disso comecei a utilizar as classes.

Caique, obrigado pela resposta! Testei a primeira parte, sem o autoload, e funcionou como voce explicou. Vou testar a segunda parte, com autoload, e dou um feedback!

O autoload nao funciona agora - https://drive.google.com/file/d/1dcLhLa3mhJb3yOvZYe72JPxxVYAMnhSU/view?usp=sharing

arquivo src/Modelo/Conta/Conta.php:

<?php

namespace Alura\Banco\Modelo\Conta;

class Conta {
}

no autoload, coloquei echo do caminho pra deixar claro qual caminho ele ta procurando. Eu nao tou entendendo pq ele nao ta achando

Parece tudo certo pelo que deu pra ver... Mas reparei algo que não da pra ter certeza pela foto...

O arquivo banco.php está dentro de qual pasta? Ele não pode estar na pasta src ou em seus sub diretórios, precisa estar em um nível superior, aparentemente o correto seria ele esta na pasta banco_projeto.

Veja se não é isso.

1: sim, esse problema que vc citou existia, e eu corrigi agora. 2: deixa eu ver se eu entendi, pq tem muitos arquivos, alguns com "use", outros sem.

Todas as classes: Cpf, Endereco, Pessoa, Funcionario, Titular e Conta.

a) Classes totalmente independentes de outras classes: Cpf e Endereco. Nao recebem outras classes como argumentos, nao herdam de outras classes e nao usam "use".

b) Classes parcialmente independentes de outras classes: Pessoa e Conta. Recebem outras classes como argumentos, mas nao herdam de outras classes e nao usam "use".

c) Classes totalmente dependentes de outras classes: Funcionario e Titular. Recebem outras classes como argumentos, herdam de outras classes (Pessoa) e ai vem a primeira confusao.

Titular precisa de: use Alura\Banco\Modelo\Pessoa; use Alura\Banco\Modelo\Cpf; use Alura\Banco\Modelo\Endereco; Se eu tiro os "use" de Titular, ele nao funciona.

Mas Funcionario funciona sem os "use". Em banco.php, fiz a classe "Funcionario" pegar apenas usando: use Alura\Banco\Modelo\Funcionario;

O que eu quero dizer eh: Cpf e Endereco se comportam de forma similar com suas necessidades de "use". Pessoa e Conta tambem.

O que nao eh possivel para a classe Titular, que precisa dos "use" tanto em banco.php, quanto em Titular.php.

Eu baixei o projeto do professor tambem, e notei a mesma coisa: Titular e Funcionario tem necessidades iguais (herdam de outra classe e usam outras classes como argumentos), no entanto, requerem coisas diferentes para funcionar. No projeto do professor eu tambem consegui fazer Funcionario "pegar" apenas com o "use" em banco.php, sem a necessidade dos "use" no proprio arquivo Funcionario.php, necessidade essa presente no arquivo Titular.php

Pq isso? Pq Titular precisa de mais coisas que Funcionario pra pegar, se sao praticamente iguais?

solução!

Bom, pelo que entendi sua dúvida é sobre o use...

O use só é necessário se a classe que você está tentando utilizar está em outro namespace.

Por exemplo, no arquivo banco.php não é declarado nenhuma classe, mas mesmo assim o arquivo está em um namespace, o namespace padrão é o \ e todos os arquivos fazem parte dele a menos que seja declarado algum outro.

As classes Cpfe Endereco então no mesmo namespace: Alura\Banco\Modelo. Se uma delas precisa utilizar a outra, não precisa do use por estarem no mesmo namespace.

As classes Titulare Conta então no mesmo namespace: Alura\Banco\Modelo\Conta.

Nesse caso, por exemplo, se a classe Titular Precisa utilizar a classe Cpf será necessário fazer o use Alura\Banco\Modelo\Cpf pois a classe Cpfestá em outro namespace.

Mas se a classe Conta precisa utilizar o Titular, não é necessário o use por estarem ambas no mesmo namespace.

Era isso sua questão?

Pronto! Isso esclareceu tudo!

Devido a Cpf, Endereco, Funcionario e Pessoa compartilharem o mesmo namespace, eles nao precisam do use. O mesmo acontece com a relacao Titular e Conta.

A relacao entre Titular e Cpf, Endereco e Pessoa eh diferente pois estao em namespaces diferentes. Muitissimo obrigado Caique! Tudo claro agora, obrigadao mesmo!

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software