Porque não usar MySQL Functions no PHP e algumas alternativas

Bem, no post de hoje iri discorrer sobre o porquê de não ser mais recomendado usar mais a extensão MySQL do PHP.

Além de ela estar depreciada a partir do PHP 5.5.0, ela não muito segura. Eles tentaram incluir a função mysql_real_escape_string, mas é comprovado que a segurança dela não é lá essas coisas, podendo comprometer sua base de dados a possíveis ataques hackers como SQL Injection. Outra coisa é que não há funções para comandar transações, ou seja, se você fizer alguma merda, dê seus pulos.

Muitos desenvolvedores que são contra a exclusão da extensão em futuras atualizações argumentam que o problema não é da extensão, ela até possui uma boa segurança, mas sim do desenvolvedor, que não sabe utilizar esses recursos. Porém, para o bem de todos, ainda acho que deve ser obsoleta, pois, vendo que muitos fazem merda com ela, por que correr o risco de, você usuário, utilizar de um serviço sem segurança alguma? Tudo bem que pode haver pequenas falhas do desenvolvedor, mas essas pequenas falhas podem custar a integridade dos dados dos usuários. Depois de uma votação com alguns desenvolvedores, a escolha dela acabar em alguma versão futura prevaleceu, ou seja, outro motivo para não usá-la, não haverá novas inclusões e mudanças.

Entretanto, essas pequenas falhas, muitas vezes, podem ser resolvidas com o PDO, uma ótima alternativa para o MySQL. Ela possui um ótimo sistema de Prepared Statements, com muito (ou quase nenhuma) falha na filtragem. Também possui funções para transações, o que ajuda muito o desenvolvedor, por exemplo, caso, após um inserção ocorra um erro, ela pode ser refeita com um simples PDO::rollback(). Todos esses métodos são abstratos, ou seja, você pode usá-los em diferentes tipos de banco de dados, seja um PostgreSQL, um MsSQL ou no próprio MySQL, tendo apenas que, caso migre de banco, alterar o SQL e alguns parâmetros da conexão.

Também há uma extensão com funcionalidades parecias com a do PDO e que só serve pra MySQL: a MySQLi. Ela pode ser utilizada de maneira orientada a objetos ou estruturada. Também possui Prepared Statements em funções para transações. O grande problema é justamente não funcionar com outros banco de dados.

E pra você desenvolvedor que está iniciando, não pense que quando me referi à segurança, me referi à senhas. Tá, também, mas a segurança com senhas é outra coisa no PHP. Se achou este artigo em alguma busca sobre isso, não pense que usando PDO vai aumentar sua segurança com elas. Busque por “criptografia de senhas“.

 

Testes unitários com PHPUnit

logo_phpunit Hoje falarei sobre o principal framework para a realização de testes unitários no PHP, o PHPUnit.

Testes Unitários: O que são? Qual a importância?

Basicamente os testes unitários servem para verificar a integridade comportamental de unidades do sistema.

São testes feitos pelo programador, para validar o comportamento de cada unidade do sistema, na maioria das vezes métodos, dos quais são feitos testes nas mais diferentes condições sobre eles, validando o retorno de exceções, o conteúdo do retorno de um método etc. ajudando assim o programador a encontrar mais facilmente determinados erros ou comportamentos não esperados.

Se quiser entender mais os benefícios dos testes unitários, indico este artigo do André Thiago, do blog A Arte do Software: http://andrethiago.wordpress.com/2011/04/06/as-vantagens-do-teste-unitario/

Obtenção do PHPUnit

O PHPUnit pode ser obtido através do Composer pelo seguinte pacote: phpunit/phpunit.
Ou por download do arquivo PHAR (PHP Archive):

Nomenclatura dos testes

A nomenclatura deve ser o nome da classe a ser testada mais Test.

Exemplo:
Classe a ser testada: UsersEntity
Nome do teste: UsersEntityTest

Métodos

Os métodos dos testes não receberão argumentos e devem ser públicos. A nomenclatura de conter o prefixo test, além de serem auto-descritivos.

Exemplo:
Teste de um setter de nome: testSetterForName()

Métodos setUp() e tearDown()

Esses dois métodos são como o __construct() e o __descruct():

  • setUp(): será executado assim que o teste for iniciado. Geralmente utilizado para pré-configurações, como instanciação de conexão com banco de dados para testes etc.
  • tearDown(): será executado ao final do teste. Seguindo o exemplo anterior, desfazendo as alterações feitas no banco depois do teste, desfazendo a conexão etc.

Exemplo de teste

Primeiro vamos criar uma classe para ser testada:

class User{
    private $name;
    private $email;
    private $age;

    public function setName( $name ){
        if( !is_string( $name ) ){
            throw new InvalidArgumentException( 'Name must be a string' );
        }

        $this->name = $name;

        return $this;
    }

    public function setEmail( $email ){
        if( !is_string( $name ) ){
            throw new InvalidArgumentException( 'Email must be a string' );
        }

        $this->email = $email;

        return $this;
    }

    public function setAge( $age ){
        if( !is_int( $age ) ){
            throw new InvalidArgumentException( 'Age must be an integer' );
        }

        $this->age = $age;

        return $this;
    }

    public function getName(){
        return $this->name;
    }

    public function getEmail(){
        return $this->email;
    }

    public function getAge(){
        return $this->age;
    }
}

e o teste:

class UserTest extends PHPUnit_Framework_TestCase{
    public function assertPreConditions(){
        $this->assertTrue( class_exists( 'User' ), 'Class not found: User' );
    }

    /**
     * @expectedExcpetion InvalidArgumentExcpetion
     * @expectedExceptionMessage Name must be a string
    */
    public function testSetterForName(){
        $this->assertTrue( method_exists( new User, 'setName' ), 'Method not found: User::setName()' );

        $name = 'Gabriel Jacinto';
        $user = new User();
        $setName = $user->setName( $name );

        $this->assertEquals( $setName, $user, 'Method User::setName() must return a instance of User' );
        $this->assertAttributeEquals( $name, 'name', $user, 'Attribute was not correctly setted' );
    }
    /**
     * @depends testSetterForName
    */
    public function testGetterForName(){
        $this->assertTrue( method_exists( new User, 'getName' ) );

        $name = 'Gabriel Jacinto';
        $user = new User();
        $user->setName( $name );
        $getName = $user->getName();

        $this->assertEquals( $name, $getName, 'Method User::getName() must return the name setted in setName' );
    }
}

Explicando

  • public function assertPreConditions(){
        $this->assertTrue( class_exists( 'User' ), 'Class not found: User' );
    }
    

    O método assetPreConditions server para mostrar ao PHPUnit que é necessário este teste não falhar para os outros funcionarem. Ai está sendo necessário a classe User existir.Daqui a pouco explico o método assertTrue.

  • /**
     * @expectedExcpetion InvalidArgumentExcpetion
     * @expectedExceptionMessage Name must be a string
    */
    

    O PHPUnit lê e interpreta anotações feitas nas classes. Dentre elas, é possível indicar qual será a exception esperada caso haja algum erro na classe e qual a mensagem esperada por essa exception. Isso também é possível pelo método setExptectedException.

  • $this->assertTrue( method_exists( new User, 'setName' ), 'Method not found: User::setName()' );
    

    O método assertTrue verifica se uma afirmação é verdadeira, no caso a verificação se o método User::setName() estava presente na classe User. Como segundo argumento, é passada uma mensagem de erro caso o teste não passe.

  • $name = 'Gabriel Jacinto';
    $user = new User();
    $setName = $user->setName( $name );
    

    É a instanciação de User e a utilização do método setName, passando como o argumento a variável $name, que é uma string. Mas se pode fazer testes propositalmente errados para verificar se o teste realmente falha.

  • $this->assertEquals( $setName, $user, 'Method User::setName() must return a instance of User' );
    

    O método assertEquals verifica se dois valores são iguais, no caso verifica se o retorno do método setName é a própria instância da classe, no caso uma técnica chamada de Fluent Interface. Como terceiro argumento, é passada uma mensagem de erro caso o teste falhe.

  • $this->assertAttributeEquals( $name, 'name', $user, 'Attribute was not correctly setted' );
    

    O método assertAttributeEquals verifica se algum atributo de sua classe é igual a determinado valor. Ai testamos se a variável User::$name está com o valor de $name (Gabriel Jacinto), para verificar se foi alterada corretamente. O quarto argumento é para a mensagem de erro caso o teste falhe.

  • /**
     * @depends testSetterForName
    */
    

    Como já dito, o frameowrk interpreta anotações. @depends diz ao teste que o para ele funcionar, ele necessita que o teste indicado também funcione.

Bem, explicado tudo o que foi feito, podemos seguir com os outros testes:

/**
 * @exptectedException InvalidArgumentException
 * @exptectedExceptionMessage Email must be a string
*/
public function testSetterForEmail(){
    $this->assertTrue( method_exists( new User, 'setName' ) );

    $email = 'gamjj74@hotmail.com';
    $user = new User();
    $setEmail = $user->setEmail( $email );

    $this->assertEquals( $setEmail, $user, 'Method User::setEmail() must return a instance of User' );
    $this->assertAttributeEquals( $email, 'email', $user, 'Attribute was not correctly setted' );
}

/**
 * @depends testSetterForEmail
*/
public function testGetterForEmail(){
    $this->assertTrue( method_exists( new User, 'getName' ) );

    $email = 'gamjj74@hotmail.com';
    $user = new User();
    $user->setEmail( $email );
    $getEmail = $user->getEmail();

    $this->assertEquals( $email, $getEmail, 'getEmail must return the email setted in setEmail' );
}

/**
 * @exptectedException InvalidArgumentException
 * @exptectedExceptionMessage Age must be an integer
*/
public function testSetterForAge(){
    $this->assertTrue( method_exists( new User, 'setAge' ) );

    $age = 15;
    $user = new User();
    $setAge = $user->setAge( $age );

    $this->assertEquals( $setEmail, $user, 'Method User::setAge() must return a instance of User' );
    $this->assertAttributeEquals( $age, 'age', $user, 'Attribute was not correctly setted' );
}

/**
 * @depends testSetterForAge
*/
public function testGetterForAge(){
    $this->assertTrue( method_exists( new User, 'getAge' ) );

    $age = 15;
    $user = new User();
    $user->setAge( $age );
    $getAge = $user->getAge();

    $this->assertEquals( $age, $getAge, 'getAge must return the age setted in setAge' );
}

Este foi um pequeno exemplo com algumas demonstrações de testes, mas você pode encontrar tudo o que precisa na documentação do PHPUnit: todos os assertions, todas as anotações aceitas e muito mais.

Rodando os testes

Para rodar os testes, basta ir no terminal e indicar qual o diretório do teste e do arquivo do PHPUnit:

php vendor/phpunit/phpunit/composer/bin/phpunit Tests/UserTest.php

Bem, por hoje é só isso. Mostrei que testes unitários são importantes para o desenvolvimento de uma aplicação e um pouco do principal framework de testes unitários do PHP, que também é usado por grandes frameworks como o Zend.

Singleton + Registry – PHP

Em mais um post sobre os patterns, vou falar do Singleton, dando como exemplo o Registry, um dos mais famosos usos do pattern.

O que é?

O Singleton veio com o objetivo de permitir apenas uma única instância de um objeto, sendo o que o método mágico __construct() é privado, impedindo a instanciação. Ele é similar à uma session, meio que uma variável global em forma objeto, sendo que você pode acessá-lo de qualquer parte do projeto.

Obs.: Singleton != Static Method

Representação:

Singleton

Registry:

O Registry é uma forma de aplicação do Singleton bem interessante. Ele serve para guardar dados que serão necessários na aplicação toda, como instâncias de banco de dados, quando se precisa de mais de um (mesmo quando não se precisa, vai que no futuro seja necessário…).

Lembrado que o Registry pode ser aplicado de outras formas, não só com Singleton, é que está é a maneira mais usada.

Implementação: Singleton + Registry

A implementação funcionará da seguinte maneira:

    class Registry{
        /**
         * Conjunto de dados
         * @var instanceof ArrayObject
        */
        private $data;

        /**
         * Aqui ficará a instancia do próprio objeto
         * @var instanceof self
        */
        private static $instance;

        /**
         * Private para não permitir ser instanciado
        */
        private function __construct(){ $this->connections = new ArrayObject; }

        /**
         * Cria a instancia do objeto
         * @return instanceof Registry
        */
        public static function getInstance(){
            if( !( self::$instance instanceof Registry ) ){
                self::$instance = new Registry;
            }

            return self::$instance;
        }

        /**
         * Adiciona um dado ao Registry
         * @param $key Nome para reconhecimento de determinado registro
         * @param $value Registro
         * @throws InvalidArumentException Caso a key já tenha sido adicionada
        */
        public function addElement( $key, $value ){
            if( $this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" have already added', $key ) );
            }

            $this->data->offsetSet( $key, $value );
        }

        /**
         * Remove um dado do Registry
         * @param $key O nome relacionado ao registro
         * $throws InvalidArgumentExcpeiton Caso o nome não esteja associado a algum registro
        */
        public function removeElement( $key ){
            if( !$this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" not found', $key ) );
            }

            $this->data->offsetUnset( $key );
        }

        /**
         * Retorna um registro
         * @param $key Nome de registro do elemento
         * @return Algum valor registrado
         * $throws InvalidArgumentExcpeiton Caso o nome não esteja associado a algum registro
        */
        public function getElement( $key ){
            if( !$this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" not found', $key ) );
            }

            return $this->data->offsetGet( $key );
        }

        /**
         * Retorna se um determinado nome de registro existe
         * @param $key Nome de registro do elemento
         * @return bool
        */
        public function existsElement( $key ){
            return $this->data->offsetExists( $key );
        }
    }

Uso:

Agora você pode, por exemplo, guardar aqui quantas conexões com alguns banco de dados você quiser:

$registry = Registry::getInstance();

$connection1 = new PDO( 'mysql:host=localhost;dbname=db_test', 'user', 'pass' );
$connection1 = new PDO( 'mysql:host=localhost;dbname=db_test2', 'user', 'pass' );

$registry->addElement( 'connection_1', $connection1 );
$retistry->addElement( 'connection_2', $connection2 );

Então em qualquer lugar de sua aplicação você pode fazer isso:

$registry = Registry::getInstance();

$connection = 'connection_1';

if( $registry->existsElement( $connection ) ){
    $pdo = $registry->getElement( 'connection_1' );//Ou connection_2
}

Conclusão:

Como visto, ele ajuda bastante quando se precisa de um objeto que transite por toda aplicação sem ser necessário instanciá-lo toda vez.
O Singleton também é bastante usado por alguns frameworks ao transitar dados relacionados às conexão (não me refiro ao Registry).

INTRODUÇÃO – FACEBOOK SDK PHP #1

DGHDGHF

Nesse post vou falar sobre a Facebook SDK PHP, a API em PHP do Facebook.

Uma breve explicação do próprio Facebook sobre do que se trata essa SDK:

O SDK do Facebook para PHP permite aos desenvolvedores implementar um rico conjunto de funcionalidades do lado do servidor para acessar a API do Facebook. Isso inclui acesso a todos os recursos da API Graph e FQL. Ele também trabalha em conjunto com o JavaScript SDK para ajudar a implementar o login com o Facebook.

O SDK é tipicamente utilizado para realizar operações como um administrador aplicação, mas também pode ser utilizado para realizar operações em nome do utilizador actual sessão. Ao eliminar a necessidade de gerenciar tokens de acesso manualmente, o SDK simplifica muito o processo de autenticação e autorização de usuários para seu aplicativo.

Para a utilização dessa API é necessário ter um aplicativo cadastrado na sessão de desenvolvedores do Facebook, mas especificamente aqui: http://developers.facebook.com
gjkjg

Instalando:

A instalação pode ser feita por Composer:

{
    "require": {
        "facebook/php-sdk":"*"
    }
}

ou pelo repositório no GitHub do Facebook.

Utilizando:

Para utilizar é necessário duas chaves de acesso que estão no painel do App:

ghjghjgh
Depois disso, a instancia da API ocorre da seguinte maneira:

use Libs\FacebookSDK\Facebook;

$appConfig = [
    'appId' => 'APP_ID',
    'secret' => 'APP_SECRET'
];

$fb = new Facebook( $appConfig );

No próximo post sobre essa API, irei mostrar como fazer o login de usuários.

Instalando e utilizando o Composer

logo-composer-transparent

O que é o Composer? Basicamente um gerenciador de dependências (ou de pacotes). Com ele, você defini qual bibliotecas vocês usará em seu projeto e ele se encarregará de incluí-las em sua aplicação. Instalação: Para instalá-lo via cUrl, abra o terminal e digite o seguinte comando:

curl -sS https://getcomposer.org/installer | php

baixando assim o composer.phar, o arquivo que se encarregará das inclusões de bibliotecas. Se quiser mais detalhes sobre a instalação, entre aqui:

Utilizando: Para utilizar, basta você criar um arquivo JSON chamado composer.json com as bibliotecas a serem utilizadas. Vamos supor que vcê irá utilizar em sua aplicação o PHPUnit e o Doctrine:

{
    "name": "Nome da sua aplicação",
    "require": {
        "phpunit/phpunit": "3.7.*",
        "doctrine/orm": "2.3.*"
    }
}

Todos os fornecidos pelo Composer você encontra aqui: https://packagist.org

Então rode o seguinte comando no terminar, estando na pasta onde está o composer.json:

php composer.phar install

Autoload: Para utilizar essas bibliotecas, o Composer possui um arquivo chamado de autoload.php, que inclui as que você necessita, então basta você fazer isso:

include_once 'vendor/autoload.php';

e pronto, todas as bibliotecas estão incluídas.

O post foi bem simples, sem se aprofundar muito no Composer, mas você pode conferir muito mais recursos aqui: http://getcomposer.org/doc/

Conexão com banco de dados implementando Abstract Factory – PHP

Bom, nessa postagem vou mostrar um exemplo de conexão com o banco de dados implementando o pattern Abstract Factory.

Com esse pattern, é possível fazer alterações no sistema sem precisar de grandes mudanças, apenas, digamos, instanciar um objeto no lugar de outro, tendo uma família de objetos que trabalham de forma transparente. Por exemplo: você tem uma aplicação com banco de dados MySQL, mas você decide trocar de SGBD, para um PostgreSQL. O que fazer? Alterar todo o código de conexão para as necessidades do PostegreSQL? Não! Com esse pattern você só precisa mudar a instância de MySQL para PostgreSQL. Vamos à implementação? Bem, a conexão de cada banco é parecida, então podemos criar uma classe abstrata pra isso:

abstract class AbstractConnectorConfig{
    private $host;
    private $dbname;
    private $user;
    private $password;

    public function __construct( $host, $dbname, $user, $password ){
        $this->host = $host;
        $this->dbname = $dbname;
        $this->user = $user;
        $this->password = $password;
    }

    public function getHost(){
        return $this->host;
    }

    public function getDbname(){
        return $this->dbname;
    }

    public function getUser(){
        return $this->user;
    }

    public function getPassword(){
        return $this->password;
    }

    abstract public function getDsn();
}

Agora para uma configuração de conexão com MySQL:

class MySQLConnectorConfig extends AbstractConnectorConfig{
    public function getDsn(){
        return sprintf( 'mysql:host=%s;dbname=%s',
            $this->getHost(),
            $this->getDbname() );
    }
}

e para PostgreSQL:

class PostgreSQLConnectorConfig extends AbstractConnectorConfig{
    public function getDsn(){
        return sprintf( 'pgsql:host=%s;dbname=%s',
            $this->getHost(),
            $this->getDbname() );
    }
}

Mas para conexão também é necessário um conectador:

interface Connector{
    public function connect( AbstractConnectorConfig $connectorConfig );
    public function disconnect();
    public function isConnected();
    public function getConnection();
}

O principal do PHP é o PDO, então:

class PDOConnector implements Connector{
    private $instance;

    public function __construct( AbstractConnectorConfig $connectorConfig ){
        $this->connect( $connectorConfig );
    }

    public function connect( AbstractConnectorConfig $connectorConfig ){
        if( !$this->isConnected() ){
            $this->instance = new PDO( $connectorConfig->getDsn(),
                $connectorConfig->getUser(),
                $connectorConfig->getPassword() );
            $this->instance->setAttribute( PDO::ATTR_ERRMODE,
                    PDO::ERRMODE_EXCEPTION );
    }
}

    public function disconnect(){
        if( $this->isConnected() ){
            $this->instance = null;
    }
}

    public function isConnected(){
        return ( $this->instance instanceof PDO );
    }

    public function getConnection(){
        return $this->instance;
    }
}

E a implementação fica muito mais simples:

$connector_config = new MySQLConnectorConfig( 'localhost',
                                              'dbname',
                                              'root',
                                              '' );

$connector = new PDOConnector( $connector_config );

e se quiser mudar pra PostgreSQL:

$connector_config = new PostgreSQLConnectorConfig( 'localhost',
                                                   'dbname',
                                                   'root',
                                                   '' );

$connector = new PDOConnector( $connector_config );

Conclusão:

Viram? O código não sofre quase que nenhuma modificação! E o conectador não sabe nem em que banco está se conectando.

Para se aprofundar mais sobre este pattern e muitos outros, aconselho o livro Head First – Design Patterns.

Observer Pattern – PHP

Olá pessoas!

Este é primeira postagem do meu blog e irei falar um pouco sobre o pattern Observer.

Este padrão vai definir uma dependência de um objeto para muitos. “Oh, mas como assim? Exemplifique!”. Ok:

Vamos imaginar uma revista. O que uma revista precisa? De assinaturas! E vocês não concordam que quando essa revista lançar uma nova edição, essa edição será entregue na mesma hora para todos os assinantes (não na mesma hora, mas vai ser enviada na mesma hora)? E que qualquer destes assinantes tem o direito de cancelar essa assinatura a qualquer momento? Então, o pattern Observer é exatamente isso!

Para esse pattern, são necessárias dois participantes: Subject (revista) e Observer (assinante).

interface Subject{
    public function attach( Observer $observer );
    public function dettach( Observer $observer );
    public function notify();
}

attach: Adiciona um observador
remove: Remove um observador
notify: Notifica os observadores

interface Observer{
    public function update( Subject $subject );
}

update: Recebe as atualizações do sujeito

Definidas as interfaces dos nossos participantes, vamos ao código da revista:

class Magazine implements Subject{
    private $subscribers = array();
    private $edition;

    public function __construct( $name ){
        $this->name = $name;
    }

    public function attach( Observer $observer ){
        if( $this->isSubscriber( $observer ) ){
            throw new InvalidArgumentException( 'Subscriber has already added' );
        }

        $this->subscribers[] = $observer;
    }

    public function dettach( Observer $observer ){
        $key = array_search( $observer, $this->subscribers );

        if( $key !== false ){
            unset( $this->subscribers[ $key ] );
        }
    }

    public function notify(){
        foreach( $this->subscribers as $subscriber ){
            $subscriber->update( $this );
        }
    }

    public function newEdition( $edition ){
        $this->edition = $edition;
        $this->notify();
    }

    public function getEdition(){
        return $this->edition;
    }

    public function getName(){
        return $this->name;
    }

    private function isSubscriber( Observer $observer ){
        return in_array( $observer, $this->subscribers );
    }
}

E o código do assinante:

class Subscriber implements Observer{
    private $name;

    public function __construct( $name ){
        $this->name = $name;
    }

    public function update( Subject $subject ){
        $magazine = $subject->getName();
        $edition = $subject->getEdition();

        echo $this->name . ' is reading edition number ' . $edition . ' of the ' . $magazine;
    }
}

Agora vamos simular:

$subscriber1 = new Subscriber( 'Gabriel' );
$subscriber2 = new Subscriber( 'João' );

$magazine = new Magazine( 'Olhe' );
$magazine->attach( $subscriber1 );
$magazine->attach( $subscriber2 );
$magazine->newEdition( '12' );
$magazine->dettach( $subscriber2 );
$magazine->newEdition( '13' );

//12:
//Gabriel is reading edition number 12 of the Olhe
//João is reading edition number 12 of the Olhe
//13:
//Gabriel is reading edition number 13 of the Olhe

Conclusão: com esse padrão, como vimos, é possível compartilhar um determinado recurso com vários participantes de uma vez só, sem se preocupar se vai faltar algum.
Bom, então é isso, nos vemos por ai com mais algum artigo 🙂