Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Paulo Nasc.

[Resolvido] Namespace

Recommended Posts

Então agora é assim? Vê alguém fazendo uma cagada, você não fala nada? Ou pior, incentiva?

 

Ninguém fez cagada,

Isso não foi questionado, a maioria das vezes que tentei fazer o melhor possível em relação as perguntas, aos possíveis problemas futuros, e implementações melhores, eu fui criticado .. também não incentivei ninguém, respondi o que foi perguntado, até acho melhor agente parar por aqui

Compartilhar este post


Link para o post
Compartilhar em outros sites

Acontece, Henrique, que utilizando-se dessa técnica você consegue abolir o autoload tornando o sistema, seja ele qual for, três vezes mais rápido.

 

 

Também concordo que, por questões de organização, cada classe deva estar em um arquivo distinto. Mas como cada caso é um caso, aí fica por conta do autor do tópico ver qual é a melhor solução pro caso dele.

 

 

Já que você quer evitar o autoload por considerar que ele faz a aplicação perder performance, eu acho melhor você deixar as classes em cada arquivo separado e abusar do uso dos requires.

 

 

Aí aparece a questão.

 

Será que perfomance é mais importante que organização?

 

Entre as duas eu fico com as duas. :P

 

Você pode muito bem deixar uma classe por arquivo, e manter a perfomance. Uma das alternativas é utilizar um Class Map, não sei se alguém citou isso, mas não li todos os posts.

 

E é exatamente isto que será usado na nova versão do ZF: http://framework.zend.com/wiki/display/ZFDEV2/Proposal+For+Autoloading+In+ZF2#ProposalForAutoloadingInZF2-ClassMapAutoloader

Compartilhar este post


Link para o post
Compartilhar em outros sites

Acho que o pessoal não entendeu a ideia do Bruno.

 

Talvez em algum momento neste tópico tenha ficado mal explicado, mas a ideia dele é que, durante o desenvolvimento, se coloque uma classe por arquivo, como é o padrão.

 

Ao final, ou seja, na hora de implantar, faz-se uma "pseudo compilação" (automatizada), que seria fazer esta união de todas as classes do mesmo namespace em um arquivo só, mas sem perder as classes separadas. Ou seja, na hora de fazer manutenção, faz-se nos arquivos separados e depois se refaz a "compilação". Na verdade isto é algo que já se faz em linguagens compiladas. Seria mais ou menos a mesma ideia.

 

Eu fiz profile de alguns projetos meus com o Zend Framework. Não tenho o resultado aqui, mas lembro que o processo que levava mais tempo era o Loader de classes do ZF. Então, se a gente conseguir diminuir o impacto deste processo, talvez seja interessante. Não sei se seria realmente interessante, mas é algo que vale a pena avaliar, não acham?

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

No tempo que eu havia analisado no Zend Server, apenas o autoloader era mais de 300ms do tempo total de processamento. Eu creio que esse tempo ser é alto é pq era um HD normal, um SSD talvez ficaria apenas 1/3 desse tempo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É interessante como as coisas são.

 

Já pararam para pensar que há 5~6 anos atrás, esse tipo de discussãoseria inimaginável em um fórum de PHP! Tenho certeza que se surgisse uma discussão sobre empacotamento em PHP, alguém certamente diria: "Isso é coisa de Java".

 

O mais interessante é que, com a evolução da linguagem, problemas que até então não existiam, passam a existir:

 

Namespaces:

 

Quero definir um namespace pra varias classes ao mesmo tempo, sendo que todas elas estariam dentro ou incluídas(include/require) em um único arquivo pra não precisar fazer isso ai de cima pra cada nova classe ou função.

 

A primeira coisa que se precisa compreender é que namespace é a definição de contexto em linguagens de programação. Definir todas as classes dentro de um único namespace é um erro pois, dessa forma, perde-se a definição de contexto e, consequentemente, o sentido de se utilizar um namespace.

 

Não coloque 2 classes por aquivo.

 

Ai surge um outro problema: Porque colocar apenas 1 classe em um arquivo? Porque não devemos colocar mais do que uma classe no mesmo arquivo? Se o contexto estiver bem definido e o namespace representar corretamente aquele conjunto de classes, qual o problema em ter várias classes dentro de um único arquivo?

 

Há quem diga simplesmente: "Pois esse é o padrão!".

 

Okay, é o padrão, mas o que significa isso?

 

Um padrão não surge ao acaso. Antes de se ter um padrão, teremos sempre um problema. Esse problema chama-se manutenção: Escrever código orientado a objetos é difícil, mas manter um código desorganizado é impossível (do ponto de vista do alto custo de manutenção).

 

Vamos imaginar a seguinte situação: Temos um sistema de armazenamento de arquivos, fazemos download do arquivo "class" de uma origem, depois fazemos o download do arquivo "class" de outra origem:

 

[neto@localhost namespace]$ ls
class  class(1)

 

Perceba que, para evitar a sobrescrita do identificador class, um nome diferente foi dado ao arquivo. Conforme as aplicações vão crescendo, identificadores homônimos vão se tornando cada vez mais prováveis e, resolver os problemas relacionados com colisão de nomes vão se tornando cada vez mais complexos. Porém, quando temos cada arquivo representando uma classe, fica mais fácil identificar o problema.

 

Imagine que tenhamos 2 diretórios: fruits e pasta:

 

Dentro do diretório fruits tenhamos os arquivos: Apple, Banana e Orange.

Dentro do diretório pasta tenhamos os arquivos: Capellini, Spaghetti e Penne.

 

[neto@localhost food]$ find
.
./pasta
./pasta/Capellini
./pasta/Penne
./pasta/Spaghetti
./fruits
./fruits/Apple
./fruits/Banana
./fruits/Orange

 

Perceba que, como os arquivos estão organizados, identificar algum problema é fácil. Não precisamos sair abrindo os arquivos que representam um namespace, basta listar os arquivos contidos em um diretório que representa um namespace que veremos as classes que estão contidas nesse namespace.

 

Porém, existe uma exceção à essa regra que justifica ter mais do que uma classe em um mesmo arquivo. Algumas vezes, uma abstração precisa oferecer uma implementação para uma operação e, quando apenas a abstração usa essa implementação, ela é feita no mesmo arquivo da abstração:

 

SomeInterface.php

<?php
namespace example;

interface SomeInterface {
public function doSomething();
}

 

Abstraction.php

<?php
namespace example;

abstract class Abstraction {
public abstract function helloWorld();

/**
 * @return	SomeInterface
 */
public function createSomething() {
	return new SomeImplementation();
}
}

class SomeImplementation implements SomeInterface {
public function doSomething() {
	echo 'Hello world!';
}
}

 

Concretion.php

<?php
namespace implementation;

use example\Abstraction;

class Concretion extends Abstraction {
public function helloWorld() {
	$this->createSomething()->doSomething();
}
}

 

Como podemos ver, Concretion usa a implementação simples oferecida pela Abstraction, mas poderia também ter sua própria implementação. Como a interface SomeInterface está separada, Concretion pode ignorar a implementação simples oferecida por Abstraction e implementar SomeInterface para ter sua própria implementação.

 

Client.php

<?php
use example\Abstraction;

class Client {
public function usesAbstraction( Abstraction $a ) {
	$a->helloWorld();
}
}

 

Para o Client, tanto faz quem ou como é implementado a operação helloWorld(), o Client apenas usa a operação e sequer tem conhecimento de que existe um outro participante envolvido. Nesses casos, quando uma implementação é oferecida por uma abstração e é usada somente por essa abstração, ter essa implementação no mesmo arquivo da abstração é completamente justificável.

 

Princípios de empacotamento:

 

Captei a idéia de vocês, seria reunir automaticamente todas as classes em um único arquivo somente para ambiente de produção. ^_^

 

Realmente interessante isso, mas somente se não forem muitas classes, se não como eu disse vai ter um desperdiço muito grande de memória aí.

 

Só espero que o mestre dos padrões (João Batista Neto) não arrance os seus cabelos ao ler este tópico. haha

 

Eu tenho créditos para arrancar os cabelos, quem já viu uma foto minha sabe do que estou falando. hehehe :P

 

Bom, agora chegamos na parte divertida da história, o empacotamento!!!

 

A primeira coisa que precisamos ter consciência aqui é que não existe mágica. Conforme a aplicação vai crescendo, a alta granularidade das classes pode se tornar um problema e, para isso, utilizamos um sistema de organização de nível mais alto. Um pacote é como uma caixa que contém vários recursos de um sistema, esses recursos devem ser agrupados de forma lógica e segundo alguns princípios.

 

:seta: Quem desenvolve aplicações orientadas a objetos e nunca ouviu falar de reutilização, que jogue o primeiro objeto.

 

Reutilização não é copiar um código de um lugar e colar em outro, reutilizar também não é fazer um clone um arquivo de um repositório para um projeto e utilizá-lo na aplicação. Reutilização é a capacidade de se utilizar um código sem precisar ler código. É a capacidade de se utilizar um código dependendo exclusivamente da abstração, sem precisar saber como é feita a implementação.

 

Sabemos que devemos depender de abstrações e não de implementações. E quando aos pacotes?

 

Quando utilizamos uma biblioteca empacotada, estamos utilizando as funcionalidades que ela oferece, por exemplo, eu desenvolvo um framework para construção de GUI:

 

gallery_94216_5_3958.png

 

Se eu empacotar esse framework e vendê-lo como uma biblioteca, o que você, como consumidor desse produto, espera?

 

Você espera que, caso eu lance uma nova versão dessa biblioteca, você possa se beneficiar dessa nova versão, você possa decidir entre utilizar a versão atual ou a nova versão, você espera ainda que a migração para a nova versão possa ocorrer apenas no momento que você tiver tempo disponível. De fato, você espera que possa utilizar essa versão apenas quando as mudanças que eu fiz na biblioteca interessarem à você.

 

gallery_94216_5_12123.png

 

Se você simplesmente copiar e colar um código que eu escrevi e usar na sua aplicação, você terá a preocupação em procurar as classes que eu modifiquei e fazer os ajustes necessários para que a nova versão funcione corretamente. Se eu fizer melhorias ou correções de bugs na biblioteca, você não será beneficiado de imediato por essas modificações. Com o tempo, muito possivelmente, sua versão será diferente da minha e, consequentemente, você deixará de se beneficiar dos novos recursos e bug fixes que eu fizer.

 

Mas, o que colocar em um pacote?

 

Esse problema já foi mencionado nesse tópico. Classes em um pacote são reutilizadas em conjunto, se você reutiliza uma classe de um pacote, você reutiliza todas. Esse deve ser o princípio base na hora de decidir o que colocar em um pacote. Se classes forem reutilizadas em conjunto, então elas deverão ser empacotadas em conjunto.

 

Como todo princípio, fica mais fácil perceber o fundamento quando estamos diante do problema, então, vamos ver um exemplo:

 

gallery_94216_5_32041.png

 

Perceba que, quando você for utilizar esse pacote, além de todas as classes de GUI, você estará utilizando também classes relacionadas com DBA, existe algum sentido nisso? Qual a relação existente entre um framework para construção de elementos de interface de usuário e um framework para abstração de bancos de dados?

 

Da mesma forma que temos o princípio da responsabilidade única (S.R.P.) para classes, temos o princípio da reutilização comum (C.R.P.) para pacotes, ou seja, um pacote precisa ser coeso. Se temos um pacote para construção de GUI, temos que ter dentro dele apenas coisas para construção de GUI.

 

O problema da falta de coesão é o mesmo que ocorre quando violamos o princípio da responsabilidade única. Quando uma nova versão da minha biblioteca é lançada, você terá que revalidar sua aplicação e ver se tudo está funcionando como deveria. Se eu empacoto coisas que não são coesas com o objetivo do pacote, uma mudança na minha camada de abstração com banco de dados poderá interferir na sua interface de usuário, ou seja, você será afetado por mudanças feitas em classes que você nem utiliza.

 

Manutenção de aplicações é cara, então é certo dizer que, mais importante que reutilização, é a manutenibilidade.

 

Uma mudança em uma classe do pacote afeta todas as classes do pacote então, de fato, classes em um pacote devem ser coesas, mas também precisam ser empacotadas de acordo com o tipo de mudança que elas sofrem. Se tivermos classes que mudam juntas, então elas devem ser empacotadas juntas, isso minimiza o esforço no processo de revalidação do sistema. Se tivermos N classes em um pacote e apenas 10 mudarem, todo o pacote precisará ser revalidado, por outro lado, se as 10 classes forem colocadas juntas em outro pacote, tudo fica mais simples.

 

Pacotes no PHP:

 

Desde a versão 5.3 o PHP introduziu nativamente os pacotes Phar. Um pacote Phar é, de certa forma, parecido com um pacote jar do Java.

 

Qual era a dúvida do tópico mesmo?

 

sendo que todas elas estariam dentro ou incluídas(include/require) em um único arquivo

 

hummmm....

 

What is phar? Phar archives are best characterized as a convenient way to group several files into a single file...

 

Então, além de já sabermos alguns princípios de empacotamento, temos uma solução nativa para o problema!!!

 

Vamos ver isso de perto, vamos empacotar nosso framework de gui:

 

[neto@localhost example]$ find com/imasters/gui
com/imasters/gui
com/imasters/gui/Component.php
com/imasters/gui/Composite.php
com/imasters/gui/Panel.php

 

Como podemos ver, temos 3 arquivos. Vamos escrever o código responsável pelo empacotamento:

 

<?php
$phar = new Phar( 'com.imasters.gui.phar' , 0 );
$phar->startBuffering();
$phar->buildFromIterator( new DirectoryIterator( 'com/imasters/gui' ) , '.' );
$phar->setStub( <<<'STUB'
<?php
Phar::interceptFileFuncs();

spl_autoload_register( function( $class ) {
$file  = 'phar://' . __FILE__ . '/';
$file .= implode( DIRECTORY_SEPARATOR , explode( '\\' , $class ) );
$file .= '.php';

if ( is_file( $file ) ) {
	require_once $file;
}
} );

__HALT_COMPILER();
STUB
);

$phar->stopBuffering();

 

O resultado disso é um arquivo chamado: com.imasters.gui.phar. Utilizando o arquivo:

 

<?php
require_once 'com.imasters.gui.phar';

use com\imasters\gui\Panel;

$p = new Panel();
$p->addAttribute( 'id' , 'example' );

echo $p->draw();

 

Resultado:

[neto@localhost example]$ php test.php 
<div id="example"></div>

 

O arquivo test.php faz o include apenas no pacote da biblioteca que contém todas as classes necessárias. Se distribuirmos esse pacote, qualquer pessoa poderá se beneficiar do framework de construção de GUI e, caso eu venha a lançar uma nova versão, bastará baixar a biblioteca e revalidar a aplicação.

 

De fato, empacotamento é um tema muito interessante (extenso também) e que será cada dia mais comum no universo PHP.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só uma coisa a comentar: :clap:

Não tinha ainda analisado ainda a fundo sobre arquivos Phar.

 

Mas aí eu me pergunto, tantas frameworks, libs, etc, não seria mais conveniente elas usarem apenas um único arquivo phar? Assim ficaria até mais fácil efetuar cache.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Que bacana. Nem precisa-se de soluções mirabolantes, então.

 

E quanto à performance? É melhor, pior, igual? Durante a execução esse pacite não terá de ser "desfeito" para que as classes sejam encontradas ou coisa do tipo?

 

Existe a possibilidade de fazer um pacote de um pacote? Nesse seu exemplo de Frammework, seriam criados N pacotes, um para cada "módulo", por assim dizer (Controller, Session, Cache...) e um único pacote externo chamado Framework com todos eles dentro?

 

Provavelmente isso ainda deve ser errado, mas é só uma curiosidade. ^_^

Compartilhar este post


Link para o post
Compartilhar em outros sites

Existe a possibilidade de fazer um pacote de um pacote?

 

Você leu a parte dos princípios de empacotamento?

 

Nesse seu exemplo de Frammework, seriam criados N pacotes, um para cada "módulo", por assim dizer (Controller, Session, Cache...) e um único pacote externo chamado Framework com todos eles dentro?

 

Não, você definitivamente não leu a parte que falei sobre os princípios.

 

:P

Compartilhar este post


Link para o post
Compartilhar em outros sites

Essa frase aqui pode e deve ser relida:

Um pacote é como uma caixa que contém vários recursos de um sistema, esses recursos devem ser agrupados de forma lógica e segundo alguns princípios.

parabéns pela explicação.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E você leu a última frase do meu post? ^_^

 

Eu li o seu inteirinho e entendi a preocupação-mor acerca do empacotamento. Mas curiosidade, é curiosidade.

 

E se Benjamin Franklin não tivesse tido a curiosidade sobre os fenômenos naturais ocorridos durante uma tempestade? Será que teríamos eletricidade há tanto tempo quanto temos neste Universo em que vivemos?

Compartilhar este post


Link para o post
Compartilhar em outros sites
Porém, existe uma exceção à essa regra que justifica ter mais do que uma classe em um mesmo arquivo. Algumas vezes, uma abstração precisa oferecer uma implementação para uma operação e, quando apenas a abstração usa essa implementação, ela é feita no mesmo arquivo da abstração:

Realmente, tocando neste ponto me fez lembrar que em Java é possível definir uma classe dentro de outra classe, como no PHP isso não é possível, faz sentido termos mais de uma classe por arquivo.

Eu não consigo imaginar alguma aplicação prática para isso, mas certamente deve haver alguma.

 

Quando defendi o método uma-classe-por-arquivo estava pensando no caso em que se houvesse mais de uma classe no arquivo, ficaria complicado utilizar o recurso de autoloading caso o programador quisesse fazer uso daquela classe FORA daquele arquivo, mas se a idéia é fazer esse paralelo com o Java que eu citei, essa classe 'extra' teria a visibilidade package invés de public e seria usada apenas internamente. Aí sim faz sentido pra mim, me corrijam se estou enganado.

 

Agora, do post inteiro, eu só não entendi essa construção:

$phar->setStub( <<<'STUB'
<?php
Phar::interceptFileFuncs();

spl_autoload_register( function( $class ) {
       $file  = 'phar://' . __FILE__ . '/';
       $file .= implode( DIRECTORY_SEPARATOR , explode( '\\' , $class ) );
       $file .= '.php';

       if ( is_file( $file ) ) {
               require_once $file;
       }
} );

__HALT_COMPILER();
STUB
);

 

@JCMais:

Mas aí eu me pergunto, tantas frameworks, libs, etc, não seria mais conveniente elas usarem apenas um único arquivo phar? Assim ficaria até mais fácil efetuar cache.

Aí caímos naquela de novo: porque não colocar todas as classes em um arquivo só?

 

A idéia do pacote é modularizar seu código. Módulos são (deseja-se pelo menos que sejam) frouxamente acoplados e altamente coesos.

Se o framework sofrer uma alteração em algum módulo, para corrigir um bug de acesso ao Banco de Dados, por exemplo, faz sentido ter que "recompilar" tudo de novo, alterar Controller, Views, Hanlders, etc?

 

O mais correto é corrigir o bug naquele pacote em específico (banco de dados) e informar ao usuário que ele deve atualizar SOMENTE aquele pacote...

Compartilhar este post


Link para o post
Compartilhar em outros sites

so por curiosidade, este codigo so rola em servidores dedicados neh? pq muitas hospedagens hj ainda utilizam o php 5.2.8....

 

Se pesquisar bem, dá pra achar hospedagens com o PHP 5.3...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Estranho, o exemplo de compilador não funcionou aqui.

 

Primeiro porque não se aplicava exatamente à minha situação, com várias classes hierarquicaente estruturadas em vários subdiretórios, tentei trocar o Iterator pela versão recursiva, mas na hora H tentou abrir um diretório e não conseguiu permissão.

 

Enfim...

 

Troquei o Phar::buildFromIterator() por Phar::buildfromDirectory() e deu certo. Testei a operação inversa, com Phar::extractTo() e confirme todos os arquivos lá presentes.

 

Mas tentei incluir o pacote e instanciar uma das classes, e a mesma não pôde ser encontrada.

Compartilhar este post


Link para o post
Compartilhar em outros sites

A primeira coisa que se precisa compreender é que namespace é a definição de contexto em linguagens de programação. Definir todas as classes dentro de um único namespace é um erro pois, dessa forma, perde-se a definição de contexto e, consequentemente, o sentido de se utilizar um namespace.

.

.

.

Um pacote é como uma caixa que contém vários recursos de um sistema, esses recursos devem ser agrupados de forma lógica e segundo alguns princípios.

 

João Batista parabéns pela explicação! Me ajudou muito com uma dúvida relacionada.

 

Existe algum padrão para o nome do pacote? Pergunto isso com base na sua definição: com.imasters.gui.phar

 

Só uma curiosidade, qual aplicativo você usa para modelar os diagramas?

Compartilhar este post


Link para o post
Compartilhar em outros sites

O que aconteceu foi o seguinte, maioria das aplicações que desenvolvi foram pra lá com 5.2.x, então, chegou um dia que precisei dos recursos mais recentes da linguagem, que é o Namespace, stream_resolve_include_path .. daí lá não tinha esses recursos, precisava de outros também .. então eu entrei em contato com o suporte deles, e pedi pra migrar, como eles mesmo anunciam la lista de recursos de hospedagens ..

 

ef57539745fc476abeac1ae.png

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.