Skip to content

Tag Archives: php

Implementação de singleton em PHP

28-Aug-08

Durante uma discussão hoje na lista php-especialistas, eu criei um protótipo para a implementação do padrão Singleton, mas que (na minha opinião) acabou ficando bem legal… Então, para não desperdiçar o trabalho e movimentar um pouco o blog, segue o código abaixo:


<?php
abstract class Singleton {
    static private $instance = array();

    final static public function instance($module) {
        if (!class_exists($module))
            throw new Exception("Module '{$module}' not found.");
        $module = strtolower($module);
        if (!isset(self::$instance[$module]))
            self::$instance[$module] = new $module;
        return self::$instance[$module];
    }
}
?>

Quer saber como ela funciona!? Essa classe abstrata, portanto não pode ser instanciada, funciona da seguitne forma: sempre que você quiser que uma determinada classe siga ao padrão Singleton, basta chama-la sempre através da classe acima, como segue:


<?php
require_once 'Singleton.php';

class ClassA {
    public $valueA = null;
}   

class ClassB {
    public $valueB = null;
} 

try {
    $objA1 = Singleton::instance('ClassA');
    $objA2 = Singleton::instance('ClassA');
    $objA1->valueA = 'ClassA instance 1';
    echo $objA2->valueA . "\n";

    $objB1 = Singleton::instance('ClassB');
    $objB2 = Singleton::instance('ClassB');
    $objB1->valueB = 'ClassB instance 1';
    echo $objB2->valueB . "\n";

    $objC1 = Singleton::instance('ClassC'); // exception!
} catch (Exception $ex) {
    echo "<b>Singleton Error:</b> {$ex->getMessage()}";
}
?>

E o resultado do código acima é:

ClassA instance 1
ClassB instance 1
Singleton Error: Module 'ClassC' not found.

E é isso ae, espero que esse código seja útil para mais alugém. :)

UPDATE1: Definido o método Singleton::instance como final, assim ele não poderá ser alterado por nenhuma classe que extenda a classe Singleton.

UPDATE2: Na versão 5.3 do PHP, foi introduzida uma funcionalidade chamada de Late static binding que faz com que possamos criar (sem nenhuma artimanha-extravagante) uma classe Singleton, onde todas as classes que a extenderem serão Singleton também. Algo parecido com o código abaixo.


<?php
require_once 'Singleton.php';

abstract class Singleton {
    static private $__CLASS__ = __CLASS__;
    static private $instance = null;

    final static public function instance() {
        if (static::$instance == null)
            static::$instance = new static::$__CLASS__;
        return static::$instance;
    }
}
?>

E nessa caso, a implementação fica algo mais ou menos parecido com:


<?php
class ClassA extends Singleton {
    // Singleton implementation
    static private $__CLASS__ = __CLASS__;
    static private $instance = null;

    prublic $value = null;
}

$objA1 = ClassA::instance();
$objA2 = ClassA::instance();
$objA1->value = 'ClassA instance 1';
echo $objA2->value . "\n";
?>

Nessa versão 5.3, ainda dará para usar os namespaces organizando um pouco melhor essa estrutura. :)

Caçando erros de sintaxe em sistemas PHP

06-Jun-08

Como o PHP não é uma linguagem compilada, grande parte dos bugs de um sistema só são percebidos (e corrigidos) em ambiente de produção. Isso acontece principalmente se você mantém um sistema grande, pois se torna difícil validar todos os arquivos que são enviados para ambiente produção em cada nova release.

Porém, usando o PHP CLI (Command Line Interface) você consegue validar a sintaxe de um sistema inteiro de uma forma simples, fácil e rápida. Se você ainda não tem o PHP CLI instalado, você pode instalá-lo no Ubuntu (ou Debian) da seguinte forma:


$ sudo aptitude install php5-cli

Pronto, agora você tem acesso ao PHP via linha de comando:


$ php -r 'echo "Hello World!";'
Hello World!

Aproveite também dar uma olhada em todas as opções do PHP CLI porque ele tem bastante coisa legal e que podem ser muito úteis em vários casos. Uma dessas opções é o validador de sintaxe, ou seja, a opção -l.

Veja o exemplo de um arquivo sem problemas de sintaxe:


$ nl helloworld.php
     1  <?php
     2  echo 'Hello ';
     3  echo 'World!';
     4  ?>
$ php -l helloworld.php
No syntax errors detected in helloworld.php

E agora o mesmo arquivo com erros de sintaxe


$ nl helloworld.php
     1  <?php
     2  echo 'Hello '   // erro de sintaxe, faltando um ";" aqui
     3  echo 'World!';
     4  ?>
$ php -l helloworld.php

Parse error: syntax error, unexpected T_ECHO, expecting ',' or ';' in helloworld.php on line 3
Errors parsing helloworld.php

Agora você pode usar um pouco de bash junto com essa opção de checagem de sintaxe para validar todo o seu sistema PHP de uma só vez, como mostrado a seguir


$ find /var/www/meu-site -name *.php -exec php5 -l {} ";"

Claro que isso não garante que não hajam erros de lógica no seu sistema, mas encontrar e corrigir todos os erros de sintaxe antes de enviar o código para o cliente já um grande passo. É possível ainda criar um script muito mais elaborado do que o comando mostado acima, mas a idéia desse post era só mostrar o conceito mesmo.

É isso ae, abraços.

PHP Day e a sequência de Fibonacci

13-Jan-08

Ontem durante a última palestra do PHP Day houve uma certa polêmica (dentre as várias que houveram nessa palestra) a respeito de criar uma função que calculasse a sequência de fibonacci em apenas uma linha. Enfim (comentários a parte) segue a implementação das sequências de Fibonacci, Tribonacci e Tetranacci com apenas uma linha em PHP.

Este código também está disponível em: http://arthurfurlan.org/phpday/fibonacci.txt

Sequencia de Fibonacci


function fibonacci($n) {
    return $n < 0 ? false : $n < 2 ? $n : fibonacci($n-1) + fibonacci($n-2);
}

Sequencia de Tribonacci


function tribonacci($n) {
    return $n < 0 ? false : $n < 3 ? fibonacci($n) : tribonacci($n-1) + tribonacci($n-2) + tribonacci($n-3);
}

Sequencia de Tetranacci


function tetranacci($n) {
    return $n < 0 ? false : $n < 4 ? fibonacci($n) : tetranacci($n-1) + tetranacci($n-2) + tetranacci($n-3) + tetranacci($n-4);
}

* Obs.: o algoritmo ficou um pouco maior que o convencional porque está tratando o caso de números negativos, que são inválidos.

Primeiro PHP Day Curitiba

22-Dec-07

Idealizado incialmente pela lista de usuários PHP de Curitiba e com a intenção fomentar a comunidade local, está marcado o primeiro PHP Day Curitiba.

Com entrada gratuita, o evento conta com o apoio da UFPR e será realizado dia 12 de Janeiro de 2008 no Departamento de Informática da UFPR no Centro Politécnico.

Acredito que o evento será bem interessante e com certeza estarei lá. :)

Onde? http://www.inf.ufpr.br/dinf/localendpostal.html
Quando? 12 de janeiro de 2008
Como chegar? http://www.inf.ufpr.br/dinf/localcomochegar.html

Maiores informações: http://gpphp-pr.blogspot.com
Veja também a chamada oficial para o evento

UPDATE: Adicionei uma nova sessão no meu site disponibilizando o material da minha palestra

Configurando PHP5 sobre FastCGI no Apache

11-Dec-07

Eu trabalhei por mais de 3 anos como desenvolvedor PHP e durante todo esse tempo uma das coisas que sempre notei foi a facilidade em driblar a segurança de quase todos os servidores web por onde passei. Quase todas as empresas (sem citar nomes) se preocupam muito com a segurança externa de suas redes, mas a segurança interna de seus servidores (quando há) é extremamente falha.

O que pretendo mostrar nesse post, é um modelo de configuração para servidores Apache e PHP que trate todos os problemas de segurança com os quais EU já me deparei e comentar um pouquinho o porquê de cada coisa. Se você tiver alguma outra idéia, crítica ou sugestão que possa enriquecer o modelo proposto aqui, sinta-se à vontade para faze-lo. :)

Como eu geralmente utilizo distribuições Debian ou Ubuntu, os comandos apresentados abaixo estarão no formato padrão para essas duas distribuições, porém você conseguirá adequar facilmente o conteúdo deste post para qualquer outro “sabor” de Linux.

Por que utilizar o FastCGI?

Ok, então vamos começar explicando um pouco do porquê rodar o PHP sobre o FastCGI ao invés de utiliza-lo como um módulo normal do Apache.

O FastCGI é um módulo do Apache que, juntamente com o SuEXEC, permite especificar ao servidor qual usuário utilizar para executar os scripts CGI, ou seja, você consegue criar um usuário Unix para cada site rodando no seu servidor e forçar o apache a executar esses scripts sempre sob um mesmo usuário. Dessa forma, além da segurança tradicional proporcionada pelo Apache e PHP, você ainda tem a segurança do Unix restringindo as ações desse usuário dentro do sistema. Portanto a partir de agora esqueça os antigos usuários globais www-data, apache, etc.

Instalação dos pacotes

A primeira coisa a ser feita é instalar os pacotes necessários pelo servidor, que nesse caso são basicamente três: Apache, FastCGI e PHP5. Também é interessante instalar o pacote php5-suhosin que propociona algumas configurações de segurança adicionais ao PHP. Apesar do módulo suEXEC estar contido no pacote libapache2-mod-fcgid, ele não será habilitado no Apache e isso terá que ser feito manualmente, como segue abaixo:


$ aptitude install apache2 libapache2-mod-fcgid php5-cgi php5-suhosin
$ a2enmod suexec
$ /etc/init.d/apache2 reload

Configurando um novo site (Passo 1/3)

Como todos os sites terão seu próprio usuário Unix, o primeiro passo da configuração de um novo site consiste na criação deste usuário. Como a única função deste usuário será acessar o site e executar o PHP via CGI, podemos colocar os arquivos do site dentro do seu diretório home e ainda (por questões de segurança) remover o seu shell, assim como mostrado abaixo:

* Por motivos didáticos, irei utilizar como exemplo meu próprio site.


$ useradd --home /var/www/arthurfurlan.org --shell /bin/false --create-home arthurfurlan

Depois de criado o usuário arthurfurlan, precisamos criar os diretórios que serão utilizados pelo Apache e PHP, que são basicamente 4 diretórios:

  • o diretório html que armazenará o conteúdo do site.
  • o diretório session que armazenará os arquivos de sessão do site.
  • o diretório tmp que armazenará os arquivos temporários do site (uploads).
  • o diretório log que armazenará os arquivos de logs do site.

Segue abaixo o comando utilizado para a criação dos diretórios:


$ cd /var/www/arthurfurlan.org
$ mkdir -p html/www session tmp log
$ chown -R arthurfurlan: html/www session tmp log
$ cd -

A principal questão a ser levantada aqui é a separação do diretório de armazenamento sessões para cada site. Isso raramente é feito e possibilita que um usuário acesse os arquivos de sessão dos demais sites que estão na mesma máquina, dessa forma se torna fácil criar um script que procure por sessões contendo senhas de usuários, de administração, etc. dos outros sites. Essa separação (juntamente com outras configurações do php) previne esse tipo de ataque.

Configurando o PHP (Passo 2/3)

Outra funcionalidade legal do FastCGI é possibilidade de se utilizar um arquivo de configuração do PHP específico para cada site, o que proporciona um maior nível de segurança. EU, particularmente, gosto de renomear o arquivo php.ini para seguir o padrão do Linux de arquivos que configuração (.*rc), porém se preferir manter o nome padrão do arquivo php.ini, sinta-se á vontade.

Vamos copiar o arquivo de configuração default do PHP:


$ cp /etc/php5/cgi/php.ini /var/www/arthurfurlan.org/.php5rc

E aplicar as seguintes configurações:

  • safe_mode para on
  • safe_mode_include_dir para on
  • disable_functions para escapeshellarg, escapeshellcmd, exec, shell_exec, curl_exec, system, passthru, popen, proc_open, proc_close, dl, ini_alter, parse_ini_file, show_source, highlight_file
  • enable_dl para off
  • open_basedir para /var/www/arthurfurlan.org/html/www:/var/www/arthurfurlan.org/tmp:/var/www/arthurfurlan.org/session
  • upload_tmp_dir para /var/www/arthurfurlan.org/tmp
  • session.save_path para /var/www/arthurfurlan.org/session

* Eu não pretendo comentar o porquê de cada uma das configurações acima, senão o post vai acabar muito mais longo (e chato) do que já está! Caso você queira entender melhor alguma das delas, pergunte na sessão de comentários abaixo. :)

Devido à uma restrição do FastCGI, o arquivo que irá efetivamente executar o arquivo PHP através do CGI precisa pertencer ao mesmo usuário que está executando o CGI, no nosso caso, precisa pertencer ao usuário arthurfurlan. Esse arquivo precisar ser um shell script com permissão de execução, como mostrado abaixo:


$ cd /var/www/arthurfurlan.org
$ touch .php5cgi
$ chmod 755 .php5cgi
$ chown arthurfurlan: .php5cgi
$ cd -

Contendo o sequinte conteúdo:


$ vim /var/www/arthurfurlan.org/.php5cgi
#!/bin/sh
export PHPRC=/var/www/arthurfurlan.org/.php5rc
exec /usr/bin/php5-cgi "$@"

* Se você nomeou o arquivo de configuração do PHP com outro nome, você deve alterá-lo na variável de ambiente PHPRC do script acima.

Agora toda a parte de configuração do PHP do novo site já está configurada, e fica faltando somente criar o VirtualHost e habilitá-lo.

Configurando o VirtualHost do Apache (Passo 3/3)

Eu irei instalar o site em localhost, sobre o IP de loopback normal 127.0.0.1 mas caso você esteja configurando um site com para acesso externo basta alterar os locais correspondentes para o IP que desejar.

Vamos criar o NameVirtualHost para o novo site.
Abra o arquivo /etc/apache2/sites-available/default e adicione a linha abaixo, caso ainda não exista:


NameVirtualHost 127.0.0.1

Se você está configurando um site para rodar em localhost, você precisará configurar a sua máquina para que ela saiba que ela deve responder pelo domínio seu novo domínio, e isso pode ser feito adicionando no arquivo /etc/hosts a seguinte linha:


127.0.0.1 arthurfurlan.org

Caso esteja utilizando um IP para acesso externo, você precisará configurar o seu servidor de DNS para que aponte o seu novo domínio para o seu host.
Agora criaremos o VirtualHost como mostrado abaixo:


$ vim /etc/apache2/sites-available/arthurfurlan.org
<Virtualhost 127.0.0.1>
    ServerName arthurfurlan.org
    ServerAlias www.arthurfurlan.org
    ServerAdmin webmaster@arthurfurlan.org

    PHP_Fix_Pathinfo_Enable 1
    DocumentRoot /var/www/arthurfurlan.org/html/www
    SuexecUserGroup arthurfurlan arthurfurlan

    <Directory /var/www/arthurfurlan.org/html/www>
        Options +ExecCGI
        AddHandler fcgid-script .php
        FCGIWrapper /var/www/arthurfurlan.org/.php5cgi .php
    </Directory>

    ErrorLog /var/www/arthurfurlan.org/log/error.log
    CustomLog /var/www/arthurfurlan.org/log/access.log combined
</Virtualhost>

E agora sim, tudo terminado! Basta habilitar o recém-criado VirtualHost como mostrado abaixo para ter o seu novo site rodando sob o seu próprio usuário. :)


$ a2ensite arthurfurlan.org
$ /etc/init.d/apache2 reload

Crypt class v2.1

11-Aug-07

Já há algum tempo que mantenho uma classe de criptografia no site PHP Classes.

Na época que publiquei o script, somente eu o utilizava e meu costume era de ter uma chave única de criptografia definida para todo o meu sistema, portanto essa chave era definida como uma constante da classe e não podia ser alterada dinamicamente. Porém nos últimos meses recebi email de vários usuários pedindo para que essa característica da classe fosse modificada, pois vários deles precisavam definir mais do que uma chave em seus sistemas. Então nesse final de semana, eu decidi trabalhar em cima da classe novamente para aplicar esta e mais algumas modificações que considero interessantes. Para aqueles que já utilizam a classe, por favor atualizem suas versões, e para aqueles que ainda não utilizam, está dada a dica. :)

Exemplo de Utilização


<?php
require_once 'Crypt.class.php';

$Crypt = new Crypt;
$Crypt->Mode = CRYPT_MODE_HEXADECIMAL;
$Crypt->Key = 'MY_CRYPT_KEY';
echo $Crypt->Encrypt('My data');
?>

Faça download da Crypt class v2.1

UPDATE:
Por sugestão do Leandro Camargo (nos comentários abaixo), adicionei este projeto ao Google Code e a partir de hoje estará disponível no seguinte endereço:

http://cryptclass.googlecode.com

Utilizando properties em PHP5

08-May-07

Se você trabalha com PHP Orientado à Objetos e gosta de manter seus dados encapsulados de uma forma “elegante”, você inevitavelmente acaba escrevendo métodos de set e get para os atributos privados da sua classe. Porém, a versão 5 do PHP conta com uma funcionalidade que pouquíssima gente utiliza (talvez porque a maioria dos programadores PHP não sabem programar) que são as Properties.

O que são Properties?

Uma property permite que você associe rotinas de acesso à um atributo da sua classe. Você consegue definir que sempre que houver uma atribuição sobre um determinado atributo do seu objeto, seja executado o método X ou então, sempre que esse valor for requisitado seja executado o método Y. O que acontece na realidade é que você define os métodos e regras de set e get para os seus atributos e faz com que eles sempre sejam executados de forma transparente pela sua classe, e assim você consegue utilizar normalmente seus atributos.

Veja um exemplo bem simples do conceito sendo aplicado:


<?php
class People {
    private $name;

    function __set($property, $value) {
        switch ($property) {
            case 'Name': $this->name = ucwords(strtolower($value)); break;
        }
    }

    function __get($property) {
        switch ($property) {
            case 'Name': return "Meu nome é {$this->name}."; break;
        }
    }
}

$p = new People();
$p->Name = 'ARTHUR FURLAN';
echo $p->Name;
?>

Resultado:


Meu nome é Arthur Furlan.

Repare que apesar de ter defindo o valor ‘ARTHUR FURLAN’ para o atributo, a string armazenada é ‘Arthur Furlan’. Como a atribuição ocorre dentro dos métodos __set() e __get(), você poderia ter uma infinidade de linhas, chamadas de funções e métodos tratando o valor passado antes de defini-lo no seu atributo.

Extendendo o conceito

Como você pôde ver, as rotinas de acesso do seus atributos são independentes. Você pode definir somente o método get e manter o set privado à classe, ou definir um método de set e manter o get privado (apesar de eu ainda não ter achado uma aplicação útil para esse caso =D). Isso é bastante útil, principalmente quando se está trabalhando com registros do banco de dados, pois o valor do campo ‘Id’ de um registro raramente é alterado. Então, nesse caso, seria interessante definirmos para ele, somente uma property de get e deixarmos que todas as suas atribuições sejam feitas internamente à classe.


<?php
class People {
    private $id;
    private $firstName;
    private $lastName;
    private $age;

    function __set($attribute, $value) {
        switch ($attribute) {
            case 'FirstName': $this->firstName = ucwords(strtolower($value)); break;
            case 'LastName' : $this->lastName  = ucwords(strtolower($value)); break;
            case 'Age'      : $this->age       = (int) $value; break;
        }
    }

    function __get($attribute) {
        switch ($attribute) {
            case 'Id'       : return $this->id;
            case 'FirstName': return $this->firstName;
            case 'LastName' : return $this->lastName;
            case 'Age'      : return $this->age;
        }
    }
}

$p = new People();
$p->FirstName = 'Arthur';
$p->LastName  = 'Furlan';
$p->Age       = 21;
echo "{$p->FirstName} {$p->LastName} ({$p->Age})";
?>

Observações

1. Repare que é possível atribuir valores à atributos que não existem fisicamente no seu objeto, desde que você crie uma property para tratar esse caso, assim como foi utilizado na classe acima.

2 Apesar de definir um atributo como privado e não criar método de get para ele, o programador poderá descobrir o valor do seu atributo através dos comandos print_r() ou var_dump(). Então, esse tipo de prática só não basta para manter um dado do objeto em sigilo.

Lista duplamente encadeada em PHP

22-Apr-07

Hoje eu estava conversando com o Diogo Menezes no messenger sobre como construir uma Lista Duplamente Encadeada em PHP, porque ele precisava criar uma para um trabalho dele da faculdade. Depois de algum tempo de conversa, eu acabei me empolgado com a discussão e resolvi implementar o algoritmo para ver como isso funcionava no PHP (que ao meu ver tem um suporte bem ruim à ponteiros).

Depois de algum tempo quebrando a cabeça para contornar algumas problemas do PHP (comentadas ao final do post) surgiu um primeiro protótipo funcional da lista, que segue abaixo:

Arquivo: Node.class.php

Estrutura utilizada para o armazenamento de valores. A lista na realidade é um grupo de objetos desta classe que apontam, de forma ordenada, para outros objetos desta mesma classe.


<?php
class Node {
    public $Next     = null;
    public $Previous = null;
    public $Data;

    function __construct($value) {
        $this->Data = $value;
    }
}
?>

Arquivo: DLinkedList.class.php

Classe que contém somente um atributo que é o nodo inicial da lista. Essa classe na realiade pode conter vários outros métodos que auxiliam na manutenção da sua lista.


<?php
require_once 'Node.class.php';

class DLinkedList {
    public $Head;

    function __construct($value) {
        $this->Head = new Node($value);
    }
}
?>

Arquivo: index.php

Exemplo de implementação da classe, apenas algoritmo bem simples para mostar o funcionamento.


<?php
require_once 'DLinkedList.class.php';

$List     = new DLinkedList(1);
$LastNode = &$List->Head;

for ($i=0;$i<10;$i++) {
    unset($Node);
    $Node           = new Node($i+2);
    $LastNode->Next = &$Node;
    $Node->Previous = &$LastNode;
    $LastNode       = &$Node;
}

$Node = &$List->Head;
do {
    echo "{$Node->Data}< br />";
    $Node = &$Node->Next;
} while(!is_null($Node->Next));
?>

Observações:

1. Se você é uma pessoa detalhista, deve ter notado no arquivo index.php, que o primeiro comando executado dentro do laço de criação dos nós da lista é o unset($Node); que segundo o manual oficial do PHP, Destrói a variável especificada. Ou seja, eu crio um novo nó, insiro ele na lista para logo em seguida destrui-lo!? Estranho não!? Pois é… Concordo em gênero, número e grau! Porém não é bem isso que acontece! Esse comando é realmente necessário, porque mesmo você alocando explicitamente um novo espaço de memória com new Node($i+2); o PHP não altera a referência na variável $Node e você acaba caindo um loop infinito, pois na segunda volta do laço $LastNodetem o mesmo valor de $Node e nós teremos um nó que aponta para ele mesmo.

2. Uma característica interessante do PHP neste caso, é o fato dele não ser tipado. Dessa forma é possível que essa sua lista guarda qualquer tipo de dados! Ela pode conter um inteiro no primeiro nodo, uma string no segundo, um objeto no terceiro e assim por diante…

3. Sim, eu sei que este código! Mas eu sempre tive vontade de ver como era uma implementação de lista em PHP.