Is it possible to use $this with static methods?

Asked

Viewed 351 times

5

Before questions or criticisms arise about not being able to do this simply because the methods static not having access to variables and public methods, private and protected, because they are accessible even without an instance of the class, I only say that, I already know this.

It happens that at the moment it starts to bother me a bit the simple fact of always having to instantiate a new class using the "keyword" new all the time, because sometimes I’m forced to function like this.

Let’s see, in this 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 only public, and has access to both public, private, and class protected 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();

?>

The situation can be circumvented, that is, it is not circumvented, since an instance is created using the "keyword" new, but it works, and automates the rest. The examples I’ve just given may not explain exactly why I need to avoid using the new, but perhaps these two examples here explain.

The only reason I 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 I am creating, and 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 prior instance.

1st Example - The 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 trail (forced):

<?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 is the right way.

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);


?>

Even though it works, and basically give me what I want, hacks are not exactly my strong suit, and I worry too much about good practice. Once this solution is adopted, what evils am I forgetting? Is it really acceptable ? I mean, because sometimes in creating a solution we also create new problems.

2 answers

4

From what I understand, the operation of the method ele is being a common operation of a static method. There is no real iteration with the class.

So in these cases, you could also define how 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 framework Laravel, calling for Facade.

Behold:

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 usually used to facilitate the chaining of methods and avoid the use of new, since in versions prior to PHP 5.4, cannot instantiate and at the same time apply the chaining.

  • Opa Laravel :D +1

  • Okay, thanks for the feedback, but on a first impression, it’s not exactly the intended, I just want to avoid using the new as also want to extinguish any access through it, thereby also avoiding public methods.

  • Another brief explanation is that I’ve never worked with frameworks, so I don’t know much of the terms used in them, I can understand the type of construction and methods, but I have problems with terms, I will do ,the searches to see if I can clear some dependencies.

  • @Edilson, Facade is facade. Something false. It is a facade class to have easy access of other

  • Ah, I did it yesterday, while creating the examples, I preferred not to add to that because I found irrelevant to the main situation, I anyway dispensed with the use of it by creating more methods, and by simply creating public access in private and protected methods, through new. However the example you showed seems to have a different approach. I will do tests, and I appreciate the explanation.

2

What do you call hack is actually a design pattern (Pattern design) called Singleton:

This pattern ensures the existence of only one instance of a class while maintaining a global access point to its object.

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

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

Follow an example of the Singleton standard 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 code block of your question is good practice and is correct for this pattern:

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

Related answer: Why shouldn’t we use Singleton?

  • The pattern Singleton, from what I know, allows only one instance of class. There is still a third form of "go through the problem", which I found even more inconvenient, so I preferred not to put in the examples. Be it, my goal is not to get a single instance of the class, it’s more "write the code online" to keep things in place, I partly intend to restrict any access to 3rd plan methods.

  • Putting everything aside, it’s kind of ironic, saying - is good practice and is right for this pattern - because just this week I didn’t know why I was using that pattern, and I was very afraid to use it for the connection class. As for the constructor part maybe it was because the real goal is not to get a single instance, however I will do more tests to see if I can parameterize the rest, and I will be waiting for other possible answers/suggestions - Thank you for the reply.

  • And when I said Hack, I said that thinking of one of the possible meanings of the word that means - "rapid improvement of a problem, or even patching" - maybe I should have used another word to describe the situation, but it was the first thing that came to mind.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.