Is it possible to use $ this with static methods?

5
  

Before questions or criticisms arise, it is not possible to do this, simply because static methods do not have access to public, private, and protected variables and methods, because they are accessible even without an instance of the class, I just say that, this I already know.

It happens that at the moment it's starting to bother me a little bit the simple fact that you always have to instantiate a new class by using keyword new at all times, because sometimes I'm forced to run like this.

Let's look at, for example:

<?php

class Teste {
    const EU = "<b>Disse ele:</b>\n";

    public function eu(){
        return self::EU . $this->ele();
    }
    private function ele(){
        return 'Pertenço a 3º pessoa';
    }
}

$teste = new Teste();
print $teste->eu();

?>

The first method is public only, and has access to both public, private, and protected class properties and methods, without any problems.

Next we have this:

class Teste1 {
    const EU = "<b>Disse ele:</b>\n";

    public static function eu(){
        return self::EU . $this->ele();
    }
    private function ele(){
        return 'Pertenço à 3º pessoa';
    }
}

print Teste1::eu();

What this will return is already obvious:

  

Fatal error: Using $ this when not in object context in ...

Doing this:

<?php

class Teste1 {
    static $instance;
    const EU = "<b>Disse ele:</b>\n";

    public static function get(){
        if(empty(self::$instance)):
            self::$instance = new Teste1();
        endif;
            return self::$instance; 
    }

    public static function eu(){
        return self::EU . self::$instance->ele();
    }
    private function ele(){
        return 'Pertenço à 3º pessoa';
    }
}

print Teste1::get()->eu();

?>

You can work around the situation, that is, it is not exactly bypass, since you create an instance using keyword%, but it works, and automates the rest. The examples I have just given may not explain exactly why I need to avoid using new , but perhaps these two examples explain why.

The only reason that makes me want this is that in my classes, not all methods are / should be accessible even with an instance of this class, because they are only complementary methods / articulations to the static methods that I am creating, in a class with about 10 methods, only 3 of them are accessible, and the type of access I want for this average of 3 methods is direct access, without any previous instance.

1st Example - Expected:

<?php

class Hash {

    static $hash;
    const COST = "$2y$10$";

    public static function hash_create($password){
        return crypt($password, $this->salt(self::COST)); # <--- $this
    }
    public static function hash_verify($password, $db_hash){
        $hash = crypt($password, $db_hash);
        return $this->are_equal($hash, $db_hash); # <--- $this
    }

    // métodos projectados apenas para uso interno
    private function random(){
        return md5(uniqid(), true);
    }
    private function fix_random($random){
        $encode = base64_encode($random);
        return str_replace("+", ".", $encode);
    }
    private function half_salt($size=null){
        $size = empty($size) ? 22 : $size;
        return substr($this->fix_random($this->random()), 0, $size);
    }
    private function salt($cost){
        return $cost.$this->half_salt();
    }
    private function are_equal($x, $y){
        if($x === $y):
            return true;
        else:
            return false;
        endif;      
    }

}

##   3º Método - PRETENDIDO (nada será executado)  ##

print Hash::hash_create('password');

print "<br/>";

$db_hash = Hash::hash_create('password');
var_dump(Hash::hash_verify('password', $db_hash)); # (null);


?>

2nd Example - The (forced) track:

<?php

class Hash {

    static $hash;
    const COST = "$2y$10$";

    # 1º notação
    public function create($password){
        return crypt($password, $this->salt(self::COST));
    }
    public function verify($password, $db_hash){
        $hash = crypt($password, $db_hash);
        return $this->are_equal($hash, $db_hash);
    }

    // métodos projectados apenas para uso interno
    private function random(){
        return md5(uniqid(), true);
    }
    private function fix_random($random){
        $encode = base64_encode($random);
        return str_replace("+", ".", $encode);
    }
    private function half_salt($size=null){
        $size = empty($size) ? 22 : $size;
        return substr($this->fix_random($this->random()), 0, $size);
    }
    private function salt($cost){
        return $cost.$this->half_salt();
    }
    private function are_equal($x, $y){
        if($x === $y):
            return true;
        else:
            return false;
        endif;      
    }

}

##   1º Método - NORMAL  ##
$hash = new Hash(); # <---
print $hash->create('password');

print "<br/>";

$db_hash = $hash->create('password');
var_dump($hash->verify('password', $db_hash)); # (true);


?>

This above, although it works, does exactly what I want to avoid, yet it's the right one.

3rd Example - HACK (The solution I found)

<?php

class Hash {

    static $hash;
    const COST = "$2y$10$";

    public static function instance(){
        if(empty(self::$hash)){
            self::$hash = $self = new Hash();
        }
        return self::$hash;
    }

    # 2º notação
    public static function hash_create($password){
        return crypt($password, self::$hash->salt(self::COST));
    }

    public static function hash_verify($password, $db_hash){
        $hash = crypt($password, $db_hash);
        return self::$hash->are_equal($hash, $db_hash);
    }

    // métodos projectados apenas para uso interno
    private function random(){
        return md5(uniqid(), true);
    }
    private function fix_random($random){
        $encode = base64_encode($random);
        return str_replace("+", ".", $encode);
    }
    private function half_salt($size=null){
        $size = empty($size) ? 22 : $size;
        return substr($this->fix_random($this->random()), 0, $size);
    }
    private function salt($cost){
        return $cost.$this->half_salt();
    }
    private function are_equal($x, $y){
        if($x === $y):
            return true;
        else:
            return false;
        endif;      
    }

}

##   2º Método - HACK  ##

print Hash::instance()->hash_create('password');

print "<br/>";

$db_hash = Hash::hash_create('password');
var_dump(Hash::hash_verify('password', $db_hash)); # (true);


?>

Although it works, and basically giving me what I want, hacks are not really my strong, and I worry too much about good practices. Once this solution is adopted, what evils will I forget? Is it really acceptable? I say, because sometimes, when we create a solution we also create new problems.

    
asked by anonymous 13.11.2015 / 02:58

2 answers

4

As far as I understand, the operation of the ele method is a common operation of a static method. There is no real iteration with the class.

Then, in these cases, you could also define it as static .

So you could access it as follows:

class Teste {
    const EU = "<b>Disse ele:</b>\n";

    public static function eu() {
        return self::EU . static::ele();
    }
    private static function ele(){
        return 'Pertenço a 3º pessoa';
    }
}

The use would be:

Teste::eu();

There is also a way that is used by the Laravel framework, called Facade .

See:

class Usuario
{
    public funciton setNome($nome) { $this->nome = $nome; return $this; }
    public function getNome(){ return $this->nome; }
}


class UsuarioFacade{
    public static function __callStatic($method, $arguments)
    {
       return call_user_func_array(array(new Usuario, $method), $arguments);
    }

}

The use would be:

UsuarioFacade::setNome('wallace')->getNome(); // Imprime: wallace

This is generally used to facilitate method chaining and avoid using new , since in versions prior to PHP 5.4 , you can not instantiate and apply chaining at the same time.

    
13.11.2015 / 15:13
2

What you actually call hack is a design pattern called Singleton :

  

This standard guarantees the existence of only one instance of a class, maintaining a global point of access to its object.

In this pattern there is a public static method ( getInstance() is the common name used) that always returns the same instance of the object.

The only thing that was missing for your code to meet the design pattern was to create a private constructor, so it is not possible for the object to be instantiated elsewhere outside the class itself.

Here is an example of the Singleton pattern in PHP:

class Singleton {
    private static $instance;

    private function __construct() {
    }

    public static function getInstance() { 
        if (!isset(self::$instance)) { 
            self::$instance = new self; 
        }
        return self::$instance;
    }
}

The syntax used in the last block of code of your question is good practice and is correct for this pattern:

Hash::instance()->hash_create('password');

Related Answer: Why should not we use Singleton?

    
13.11.2015 / 12:01