Ir para conteúdo

POWERED BY:

Arquivado

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

Mystic

[Artigo] Métrica organizacional de funções

Recommended Posts

Olá pessoal, espero que vocês gostem desta dica, então café em uma mão e notepad++ na outra.

Gostaria de falar sobre a maneira como podemos lidar com a organização de funções em PHP.

Muitos programadores PHP costumam incluir um arquivo com dezenas de funções, sempre que vão executar alguma ação, isso acontece no WordPress.

Incluir um arquivo com tantas funções para o PHP interpretar e colocar tudo na memória, acho uma tarefa um tanto exaustiva para qualquer servidor.

Porque não incluir a função apenas quando for necessária?

Vamos fazer duas coisas, primeiro cuidar da proteção dos arquivos php contendo cada função.

Os nomes de arquivos de função serão: 'functionName.fn.php'.

No .htaccess:

<Files ~ "\.(fn)\.php$">
order deny,allow
deny from all
</Files>

Isto protegerá o arquivo contra acesso direto.



Muito bem, agora vamos construir uma classe que será responsável pela a comunicação entre as funções.

 

<?php
class FunctionManager
{
	private $pathToFunctions = '/path/to/functions/';
	
	private $fnName = null;
	
	private $params = array();
	
	const FN_SUFFIX = '.fn.php';
	
	
	public function __construct( $fn_path, $function_name = null, $params = null )
	{
		$this->pathToFunctions = $fn_path;
		
		$this->fnName = $function_name;
		
		$this->params = $params;
	}
	
	public static function fn( $function_name, array $params = array() )
	{
		if( ! function_exists( $function_name ) )
		{
			include $this->pathToFunctions . $function_name . static::FN_SUFFIX;
		}
		
		if( is_callable( $function_name ) )
		{
			return call_user_func_array( $function_name, $params );
		}
		else
		{
			throw new InvalidArgumentException( 'Expected a valid function name' );
		}
	}
	
	
	public static function call( $function_name )
	{
		$args = func_get_args();
		
		//remove $function_name
		unset( $args[0] );
		
		$args = array_values( $args );
		
		return self::fn( $function_name, $args );
	}
}

Descupem a falta de comentários nas propriedades e métodos.

 

 

Basicamente são 3 formas de uso

1 - Argumentos passados em Array

<?php
$return = FunctionManager::fn( 'FunctionName', array( 'arg1', 'arg2' ) );

 

2 - Herdando os argumentos do método.

<?php
$return = FunctionManager::call( 'FunctionName', /*[, mixed $arg ]*/ );

 

3 - Instanciando

<?php
$FunctionManager = new FunctionManager( 'other/path/to/' );
$return = $FunctionManager->fn( 'FunctionName', array('args') );

 

Apenas instancie quando tiver que mudar o path

 


Refatore a classe para se adaptar aos seus projetos!



Compartilhar este post


Link para o post
Compartilhar em outros sites

 

Já conhecia, obrigado por compartilhar.

 

As vezes é bom cozinhar criando as receitas, o ganho com isso é tornar o gerenciamento rápido e ganhar tempo sem ter que ficar testando e entendendo um código/biblioteca externo(a).

Existem alguns casos em que usar uma biblioteca externa é o melhor a se fazer.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu já cheguei a conclusão de que o PHP não combina com funções, pelo menos até agora.. não há autoloading, as namespaces ficam extremamente estranhas, não há nenhum padrão, etc.

 

Quando uma função é necessária, eu basicamente me convenci a criar um método estático (sem nenhum acesso à estado algum, apenas input => output). Com isso, é possível seguir a PSR-0 e ter autoloading tranquilo.

 

O autoloading do PHP já faz isso tudo em lazy-loading e nem precisamos nos preocupar com esse tipo de problema.

 

Um ponto que fica muito, mas muito ruim usando isso é a usabilidade, veja bem:

// naturalmente
$return = FunctionName('arg1', 'arg2');

// usando este código
$return = FunctionManager::fn( 'FunctionName', array( 'arg1', 'arg2' ) );

 

Qual é mais agradável e fácil de manter? Eu não me preocuparia com meia dúzia de nanossegundos ganho enquanto o código fica mais complexo e difícil de ler.

 

Além do que isso não melhorará a performance. Apesar de esta técnica carregar as funções em "lazy loading", o código precisa chamar call_user_func_array, que é uma função bem lenta. Em outras palavras, você tira a bala mas enfia uma faca.

 

 

<Files ~ "\.(fn)\.php$">
order deny,allow
deny from all
</Files>

Isto protegerá o arquivo contra acesso direto.

 

Nesse caso, basta colocar os arquivos um nível acima da pasta pública. Quando menos acoplamento entre a aplicação e o servidor (seja ele Apache, Nginx, Lighthttpd) é melhor, pois possibilita uma troca de servidor sem praticamente mudar nada.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dá pra ganhar um pouquinho mais de performance por não invocar os métodos/funções através de outro método, bastando usar __call() e __callStatic().

 

E como sistema de arquivos é lento por definição, dá pra otimizar incorporando as funções ao Manager em si e só incluir o arquivo se a Closure correspondente (já que com elas é mais fácil trabalhar com aliases) não tiver sido invocada anteriormente, no fluxo vertical da Requisição.

Compartilhar este post


Link para o post
Compartilhar em outros sites

O autoloading do PHP já faz isso tudo em lazy-loading e nem precisamos nos preocupar com esse tipo de problema.

 

Um ponto que fica muito, mas muito ruim usando isso é a usabilidade, veja bem:

// naturalmente
$return = FunctionName('arg1', 'arg2');

// usando este código
$return = FunctionManager::fn( 'FunctionName', array( 'arg1', 'arg2' ) );

 

Obrigado por compartilhar sua opinião.

 

Você testou em um sistema grande e real.

 

Usabilidade não garante o desenpenho quando você lida com alto tráfego, você pode até escrever mais um pouco, mas terá um melhor resultado final.

Eu falo por experiência própria, durante alguns anos eu usei e observei um ganho de velocidade se comparado com incluir um arquivo com dezenas de funções como faz o WordPress.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isso é relativo.

 

Se por um lado você ganha performance por não fazer um mega include e carregar tudo e mais um pouco que de repente aquela Requisição não vai usar de uma só vez, ao fazer vários pequenos includes você está acessando o disco e o sistema de arquivos que são lentos por natureza várias e várias vezes.

 

Infelizmente com PHP não existe uma fórmula mágica, o que se pode fazer é otimizar tudo aquilo que for possível, desde micro-otimizações até o hardware do servidor que rodará o programa.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Concordo quase 100%.

 

O segredo de tudo é fazer cache e ter o mínimo de requisições possíveis, tanto requisições client-side quanto no PHP (requires, banco de dados, etc.) e tentar criar o código menos complexo possível e priorizar certas funções funcionais sobre um monte de loops com estado sendo modificado toda hora (isso melhora até o próprio código).

 

De resto, basta esperarmos que o PHP tenha uma nova Zend Engine com uma API unificada para que então tenhamos um compilador decente, mas isso vai levar tempo apesar de ser um futuro bem visível para a linguagem.

 

Usar print ao invés de echo (referência estúpida: http://www.phpbench.com/) não vai mudar muita coisa. Para identificar gargalos de performance basta usar o xDebug.

 

Micro otimizações são otimizações prematuras.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isso é relativo.

 

Se por um lado você ganha performance por não fazer um mega include e carregar tudo e mais um pouco que de repente aquela Requisição não vai usar de uma só vez, ao fazer vários pequenos includes você está acessando o disco e o sistema de arquivos que são lentos por natureza várias e várias vezes.

 

Infelizmente com PHP não existe uma fórmula mágica, o que se pode fazer é otimizar tudo aquilo que for possível, desde micro-otimizações até o hardware do servidor que rodará o programa.

 

Você não está se colocando no lugar de quem precisa usar funçoes durante toda a execusão do programa, cache, é claro, mas qual é o mais rápido: Um include no início com 300 funçoes e o PHP vai rodar o restante do programa em baixissima velocidade por falta de memória, ou, imagine você chamar cada função na hora certa?

 

<div class="exemplo-user">
 <img src="<?php echo $YourRegistry::call( 'UserPic' ) ?>"> <?php echo $YourRegistry::call( 'UserName' ) ?>
 <div class=""><?php echo $YourRegistry::call( 'yourSession' )[ 'session_id' ] ?></div>
</div>

Em um template, fazer include/Require misturado no código não é bom.

 

As vezes agente escreve um sistema orientado 100% a objetos, mas existem muitos programadores que usam funçoes indiscriminadamente, por isso eu dei essa dica.

 

Nesse caso, basta colocar os arquivos um nível acima da pasta pública. Quando menos acoplamento entre a aplicação e o servidor (seja ele Apache, Nginx, Lighthttpd) é melhor, pois possibilita uma troca de servidor sem praticamente mudar nada.

 

Não seja hipócrita, metade do mundo usa, e você nunca usa .htaccess? Ou prefere nunca mudar de servidor mais continuar sem trabalhar corretamente com o apache apenas para abstrair o servidor das suas aplicações.

 

Você prefere deformar completamente o teu esquema de pastas, colocando um nível acima da home só para abstrair o servidor da aplicação, imagine o seguinte:

folders.gif

 

Imagine se você tivesse quer proteger aquela pasta adapters, você removeria ela do conjunto de dezenas de pastas do teu site, só para abstrair o servidor da aplicação.

 

 

O apache é usado até pelo Google, o que você precisa fazer é modificá-lo de uma maneira que atenda o teu site.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dar um require com 300 funções não consome performance, até porque o PHP só irá as definir, elas não são executadas de primeira mão. O seu problema é com o WordPress, que tem um fórum específico aqui.

 

O problema com servidor é que como você bem falou, "metade do mundo usa", a outra não (e o cliente é quem decide o servidor). Eu só uso .htaccess para remover index.php da url. O problema não é usar o .htaccess em si, mas sim abusar dele ou usá-lo para coisas que não é necessária.

 

As pastas não ficam, de forma nenhuma, "zuadas", já que a própria pasta "application" já seria uma um nível acima e a outra seria a "public" que teria um index.php.. Isso não é uma invenção, é uma coisa bem antiga aplicada por diversas ferramentas.

 

 

O apache é usado até pelo Google, o que você precisa fazer é modificá-lo de uma maneira que atenda o teu site.

 

O Apache continua não sendo o único servidor e o Nginx pode ser uma alternativa melhor na maioria dos casos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu li não lembro onde que o nGinx é tão... "despido", da maioria dos recursos que se encontram nos outros servidores que muitas hospedagens preferem ele por causa do custo / benefício e por muitos usuários por melhor performance.

 

Não sei até onde isso é verdade pois já hospedei um WordPress num nGinx e era lento pra caramba. Talvez seja apenas com WordPress, que até arrepia só de falar, já não sei dizer.

 

Mas qual a dificuldade em manter toda aplicação um nível acima do www e nele apenas o conteúdo público? O browser vai requisitar o index a partir do público e seu o programa requerer as bibliotecas um nível acima tudo continua funcionando, com a vantagem de diminuir-se, pelo menos um pouco, os riscos de roubo de dados.

 

Eu não faço isso porque não programo profissionalmente, codifico um coisinha ou outra, eventualmente, e ainda no sistema operacional menos adequado para programação, então não compensa me preocupar com isso.

 

Quanto a questão PHP vs. diversas requisições, além do fator do sistema operacional e arquivos, tem a questão da análise. Se você em uma requisição vai usar apenas , se lá, um pacote MVC de algum framework, incluir logo de início o framework todo vai pesar no fato de que tudo aquilo vai ser no mínimo checado antes de a Requisição ser despachada.

 

É um assunto muito controverso porque esse tipo de "pacote", por fazer um único include/require é três vezes mais veloz do que vários includes.

 

Eu já fiz um benchmark simplificado disso, só não é tão fácil porque o PHP exige uma determinada "ordem" nos arquivos (os mais externos - interfaces e abstratos - definidos antes dos mais internos - classes concretas).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Disco é lento, muito lento, um dos hardwares mais lentos. Para comprovar isso, basta fazer uma simples pesquisa por arquivos em qualquer disco rígido com uns 10k arquivos, vai demorar.

 

Quando um arquivo é carregado, o PHP faz o parse, interpreta e executa. Todavia, a execução, nesse caso, é apenas uma simples definição de classes/interfaces/traits e suas APIs, nenhuma lógica complexa e que vá comprometer performance é executada.

 

Bruno, talvez o nginx não estava com uma configuração adequada.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bruno, talvez o nginx não estava com uma configuração adequada.

 

Ou, caso não tenha notado, o WordPress como denominador comum :devil:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Quando um arquivo é carregado, o PHP faz o parse, interpreta e executa. Todavia, a execução, nesse caso, é apenas uma simples definição de classes/interfaces/traits e suas APIs, nenhuma lógica complexa e que vá comprometer performance é executada.

 

Isso não era para ser citado em um artigo desse nível, otimização é algo como comer e beber água, é padrão para qualquer desenvolvedor.

 

<?php
/*
Seja tolo o bastante para usar isto, pode apostar, o WordPress usa essa, 
queira me desculpar, "porcaria" de esquema:
*/

//Nenhum cache, apenas o nativo
include 'oneThousandFunctions.php';

/*
[...]
E apenas agora, várias páginas PHP depois, ERRONEAMENTE algum sistema de cache de 30s que qualquer um pode baixar e instalar,
apenas alguns criam o cache completo, outros vão deixar isso ai acontecer
*/
/**
* [...]
*/

Afirmar o que se houve por aí é fácil, eu quero ver "codar", imagine uma empresa que lida com um milhão de requisições a cada 2 a 3 horas, a empresa não vai lhe dar tempo, você precisa ter um laboratório em casa, para testes e entregar o melhor, e agora?

 

Quando um arquivo é carregado, o PHP faz o parse, interpreta e executa.

 

Sabe o que é "aceleração inicial em um nível"?

 

É isso:

 

O novo PHP vem munido de tudo que as nossas pobres almas desejam:

 

Interpreta apenas na primeira vez, depois apenas executa tudo cacheado, apenas com PHP, nenhuma interpretação é feita nas requisições seguintes.

 

Tive a oportunidade de desenvolver, se me permite dizer, um dos melhores sistemas de cache, totalmente dinâmico, ele nunca precisa expirar.

 

No meu caso eu faço o PHP trabalhar apenas na primeira requisição, mesmo que um milhão de pessoas entrem no sistema agora, o PHP vai trabalhar apenas para o visitante numero 1, para os demais, vai ser apenas uma bela pista limpa para o PHP deslizar.

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.