Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Olá pessoal,
Estou estudando PHP OO e MVC, e depois de muito ler estou tentando praticar algumas coisas. Após ler sobre alguns conceitos, estou tentando criar um exemplo prático sobre o Design Pattern "Strategy".
Como achei muito genérico os exemplos que achei em minhas pesquisas, quis criar um exemplo a partir de uma necessidade minha, que é poder criptograr senhas de usuários de várias formas, sem ter que refatorar muito código caso o algoritimo mude.
Sendo assim, cheguei no seguinte código com base em alguns exemplos:
<?php
interface IAlgorithm
{
public function hash( $string );
}
class Bcrypt implements IAlgorithm
{
protected static $cost = 8;
protected static $salt = 'teste';
public function __construct( $cost = null )
{
self::$cost = $cost;
}
public function hash( $string )
{
echo crypt($string, '$2a$' . self::$cost . '$' . self::$salt . '$');
}
}
class Md5Hash implements IAlgorithm
{
public function hash( $string )
{
echo md5( $string );
}
}
abstract class AbstractAlgorithm
{
private $algorithm;
public function setHash( IAlgorithm $algorithm )
{
$this->algorithm = $algorithm;
}
public function hash()
{
$this->algorithm->hash();
}
}
class BCryptAlgorithm extends AbstractAlgorithm
{
public function __construct()
{
$this->setHash( new BCrypt() );
}
}
class Md5Algorithm extends AbstractAlgorithm
{
public function __construct()
{
$this->setHash( new Md5Hash() );
}
}
$bcrypt = new BCrypt( "8" );
$bcrypt->hash( "123456" );
echo "<br />";
$md5Hash = new Md5hash();
$md5Hash->hash("123456");
No exemplo acima, usei o Bcrypt e o MD5 como exemplos de criptografia. Minhas dúvidas iniciais são:
1) O exemplo aplicado acima, mesmo que apenas "ilustrativo" está correto para aplicação do padrão Strategy?
2) Mesmo usando uma interface, eu poderia criar novos métodos "auxiliares" dentro das classes que implementam os algoritimos? Por exemplo, criar um método "check()" dentro da class "Bcrypt"?
3) No meu modelo de MVC que estou estudando e fazendo alguns testes, eu inclui as classes dentro do controller, e as instanciei dentro do meu Model, ao gravar um usuário no banco, por exemplo. É errado fazer desta forma?
Agradeço a paciência de quem puder me ajudar e orientar.
Abraços
Olá Enrico,
Muito obrigado por me responder. Vou responder em partes:
Por que você os tornou estáticos?
Os tornei estáticos por que seriam usados apenas dentro da classe. Isso é uma má prática?
Hã? Se eu consegui compreender, você está instanciando o controller dentro da model?
Então, em meu pseudo-mvc tenho mais ou menos isso :
Index.php
<?php
require "controller/UserController.php";
$controller = new UserController();
$controller->save();
UserController.php
<?php
require "model/entity/User.php";
require "model/UserModel.php";
class UserController
{
public function __construct()
{
private $model;
private $user;
$this->model = new UserModel();
$this->user = new User();
}
public function save()
{
$post = filter_input_array( INPUT_POST , array(
'name' => FILTER_SANITIZE_STRING,
'senha' => FILTER_SANITIZE_STRING
) );
$this->user->setName( $post['name'] );
$this->user->setPass( $post['senha'] );
return $this->model->create( $this->user );
}
}
User.php
<?php
class User
{
protected $id;
protected $name;
protected $pass;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getPass()
{
return $this->pass;
}
public function setId( $id )
{
$this->id = $id;
}
public function setName( $name )
{
$this->name = $name;
}
public function setPassword( $pass )
{
$this->pass = $pass;
}
UserModel.php
<?php
class UserModel
{
private $db;
public function __construct()
{
$this->db = Registry::getInstance()->get( 'pdo' );
}
public function create( User $user )
{
require_once "util/Bcrypt.php";
$stmt = $this->db->prepare('
INSERT INTO `users`
(name, pass, created_at)
VALUES
(:name, :pass, NOW());
');
$bcrypt = new BCrypt( "8", "SALT AQUI" );
$stmt->bindParam( ':name', $user->getName(), PDO::PARAM_STR );
$stmt->bindParam( ':pass', $bcrypt->hash( $user->getPass() ), PDO::PARAM_STR );
if ( $stmt->execute() ) {
$user->setId( (int)$this->db->lastInsertId() );
$stmt->closeCursor();
return $user->getId();
}
return false;
}
}
Perceba que eu fiz o include da classe BCrypt dentro do método create() que está no meu Model... É correto fazer desta forma ou eu deveria fazer esse include em outra camada do MVC?
Valeu,
abss
Os tornei estáticos por que seriam usados apenas dentro da classe. Isso é uma má prática?
Neste caso o correto seria declara-los como private. Como static eles vão estar acessíveis a qualquer um que der um include na classe. Na verdade a grande sacada do static é você poder acessar métodos ou atributos de uma classe sem precisar instanciar a mesma. O ponto negativo é que isso equivale a usar globais que envenenam o código quando não bem usadas.
Perca que eu fiz o include da classe BCrypt dentro do método create() que está no meu Model... É correto fazer desta forma ou eu deveria fazer esse include em outra camada do MVC?
Evite instanciar um objeto dentro de outro. Isso gera acoplamento entre classes o que, assim como globais, é outra coisa a ser evitada. Procure usar dependency injection, neste conceito em vez de instaciar o objeto dentro da classe você vai passa-lo pelo construtor ou metodo.
Embora nos controllers você poça instanciar objetos no método, no model - como no caso do include do BCrypt na classe UserModel -o melhor seria o dependency injection, assim como você esta recebendo um objeto User no método receba também um objeto BCrypt,
O ideal seria abstrair essa tarefa de criptografar a ponto de poder receber qualquer algorítimo de hash, você pode criar uma interface hash que implementa o método hash, Assim você poderia receber objetos de qualquer classe implementa esta interface. O que importaria para esta classe é receber um objeto com metodo hash( ), ela pode permanecer "burra" em relação ao algorítimo em si. Assim ela fica flexível e pode-se usar diferentes algoritmos nela.
Neste caso o correto seria declara-los como private. Como static eles vão estar acessíveis a qualquer um que der um include na classe. Na verdade a grande sacada do static é você poder acessar métodos ou atributos de uma classe sem precisar instanciar a mesma. O ponto negativo é que isso equivale a usar globais que envenenam o código quando não bem usadas.
Acho que saquei...
>
Evite instanciar um objeto dentro de outro. Isso gera acoplamento entre classes o que, assim como globais, é outra coisa a ser evitada. Procure usar dependency injection, neste conceito em vez de instaciar o objeto dentro da classe você vai passa-lo pelo construtor ou metodo.
Embora nos controllers você poça instanciar objetos no método, no model - como no caso do include do BCrypt na classe UserModel -o melhor seria o dependency injection, assim como você esta recebendo um objeto User no método receba também um objeto BCrypt,
O ideal seria abstrair essa tarefa de criptografar a ponto de poder receber qualquer algorítimo de hash, você pode criar uma interface hash que implementa o método hash, Assim você poderia receber objetos de qualquer classe implementa esta interface. O que importaria para esta classe é receber um objeto com metodo hash( ), ela pode permanecer "burra" em relação ao algorítimo em si. Assim ela fica flexível e pode-se usar diferentes algoritmos nela.
Entendi... esse é exatamente o motivo pelo qual estou criando essa interface "Algorithm"... Acho que com sua resposta deu até para abrir mais minha mente em relação a Dependency Injection... hehhee
Puxa, obrigado Enrico e Raoni, além de sanarem minhas dúvidas, foram esclarecedores em outros pontos...
Abraços!
Você poderia simplificar um pouco. Não existe necessidade de ter uma classe AbstractAlgorithm e depois estendê-la para todo strategy. Ficaria assim:
O segundo ponto é aqui: Por que você os tornou estáticos? E como eu modificaria esse salt sem editar a classe? Você tem dois problemas: estado global desnecessário e uma violação do OCP, já que é impossível mudar o salt sem editar a classe.A solução é bem simples:
class Bcrypt implements IAlgorithm
{
O padrão em si sim.
Claro que poderia. Porém, se for algo interno torne protected/private.
A única coisa que você tem que prestar atenção é quanto ao LSP, veja o caso abaixo:
Como o método check não faz parte da interface, não se deve usar a interface como o tipo requerido, então tome cuidado com isso.Hã? Se eu consegui compreender, você está instanciando o controller dentro da model?