Using $this when not in object context

Eléphanteau du PHP | 40 Messages

11 août 2019, 18:07

Bonjour,,

Jusqu' php5.6 je me servais des appels statiques pour stocker des résultats de traitements communs dans les objets appelants:

Code : Tout sélectionner

class traitment_commun { function getParam() { $this->param = 'traitement quelconque'; } } class appelant { function __construct() traitment_commun::getParam(); echo $this->param; //'traitement quelconque' }
Et hop n'importe quel script appelant pouvait récupérer le résultat d'un traitement commun dans ses propriétés.

Mais depuis php7 la ligne $this->param = 'traitement quelconque' donne l'erreur donnée dans le sujet

Quelle est maintenant la méthode appropriée pour obtenir ce même résultat ?

Avatar du membre
Mammouth du PHP | 979 Messages

12 août 2019, 13:54

Salut, voilà une méthode qui à priori devrait fonctionner :
class traitment_commun
{

  protected static $param = null;

  public static function getParam($object)
  {
    // dans un if pour ne pas refaire le traitement quelconque à chaque appel si appelé plusieurs fois
    if (self::$param === null) {
      self::$param = 'traitement quelconque';
    }

    $object->param = self::$param;
  }
}


class appelant
{

  public $param;// facultatif, juste pour montrer que la propriété doit être publique si déclarée, sinon faire un setter

  public function __construct()
  {
    traitment_commun::getParam($this);
    echo $this->param;//'traitement quelconque'
  }
}
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 40 Messages

12 août 2019, 15:56

Merci pour cette réponse.

Ca pourrait convenir. $this pouvant être assez volumineux, sa transmission par défaut en paramètre se fait par référence maintenant, c'est bien ça ?

L'utilisation de traits ne serait pas meilleure ici ?:

Code : Tout sélectionner

trait traitment_commun { function getParam() { $this->param = 'traitement quelconque'; } } class appelant { use trait; function __construct() $this->getParam(); echo $this->param; //'traitement quelconque' }
Mais j'ai des appels à traitment_commun dans des methodes statiques qui vont réitérer l'erreur de départ j'imagine...

Avatar du membre
Mammouth du PHP | 979 Messages

12 août 2019, 17:42

Je pense que ça s'apparente plus à un pointeur qu'une référence et à priori c'est depuis php5 déjà.
https://www.php.net/manual/fr/language. ... rences.php

Avec l'énoncé difficile de dire si un trait serait une meilleur solution. Je vois ça comme quelque chose permettant d'implémenter un même comportement dans des classes différentes.
https://www.php.net/manual/fr/language.oop5.traits.php

Et effectivement si tu appelles getParam depuis une méthode static ça va poser problème puisque tu ne pourras pas lui fournir une instance d'objet. Est-il vraiment important de stocker la valeur dans une propriété ? parce que tu pourrais te contenter d'appeler simplement la méthode static quand tu as besoin d'obtenir son résultat. Disons que "traitement quelconque" c'est un peu vague aussi. Avec un problème concret je pourrais te dire concrètement ce que je ferais.
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 40 Messages

14 août 2019, 11:35

Par traitement_quelconque je veux dire qu'il y a plusieurs méthodes avec des traitements différents que j'utilise dans la classe traitement_commun et qui peuvent être relativement complexes avec notamment des récupérations de propriétés d'autres objets. Je ne les ai pas retranscris ici pour plus de clarté car ça fait 250 lignes mais je peux en afficher une partie significative si besoin.

En fait ta solution de passer l'objet appelant en paramètre semble fonctionner (je n'ai pas encore pu tester tous les cas de figure, c'est une grosse application) mais j'ai peur de perdre en performance sur le passage de $objet à chaque fois. J'ai donc défini la fonction en appel par référence:

Code : Tout sélectionner

public static function getParam(&$object)

Avatar du membre
Mammouth du PHP | 979 Messages

14 août 2019, 14:45

Les traitements dépendent-ils des propriétés de l'objet appelant ? combien de propriétés de l'objet appelant sont modifiées ? 1 seule, plusieurs ?
Parce que tu peux aussi faire comme ça par exemple :
class traitment_commun_2
{

  protected static $param = null;

  public static function getParam()
  {
    if (self::$param !== null) {
      return self::$param;
    }

    return self::$param = 'traitement quelconque';
  }
}


class appelant_2
{

  public $param;// peut être protected ou private

  public function __construct()
  {
    $this->param = traitment_commun_2::getParam();
  }
}

Il n'y pas de grosse différence entre le passage de l'objet par référence ou non, par contre si tu ne le passes pas, là la différence est déjà plus significative.

Test avec 5 fois 500 milles appels du constructeur :
sans paramètre : 1.5874841213226s
avec référence : 1.7443840503693s
sans référence : 1.7676830291748s

sans paramètre : 1.5766520500183s
avec référence : 1.7772159576416s
sans référence : 1.7496340274811s

sans paramètre : 1.5703070163727s
avec référence : 1.7504739761353s
sans référence : 1.7444298267365s

sans paramètre : 1.5739760398865s
avec référence : 1.7501800060272s
sans référence : 1.7492551803589s

sans paramètre : 1.5804479122162s
avec référence : 1.7477819919586s
sans référence : 1.7403628826141s
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 40 Messages

15 août 2019, 08:52

Très intéressant ton test. Mais je pense que le peu de différence que tu relèves avec/sans références est du à la petitesse de ton objet. Si tu prends un objet réel de plusieurs K j'imagine que la différence va croître significativement.

Concernant le nombre de paramètres, oui il y en a plusieurs et j'utilisais cette technique justement pour ne pas avoir à écrire à chaque fois l'initialisation des paramètres dans les nombreuses fonctions qui l'utilisent. Mais je me rends compte que cette petite feignantise pourrait avoir de lourdes conséquences en performances si je dois maintenant passer l'objet en paramètre...

Certains traitements communs sont indépendants de l'objet appelant mais d'autres reçoivent des paramètres en entrée. Voici quelques exemples de traitements réel pour fixer les idées (modifiés avec la technique de transmission d'objet que tu m'as inspirée):

Code : Tout sélectionner

/** * Get Common filters * Sets peid and exercice filters from Jrequest in caller's $this context * */ function getCommonFilters( &$o ) { $jinput = new JInput(); $o->filter_peid = $jinput->get( 'filter_peid', 0, 'integer' ); $o->filter_exercice = $jinput->get( 'filter_exercice', edfHelper::get_last_exercice(), 'string' ); $o->filter_theme_id = $jinput->get( 'filter_theme_id', 1, 'integer' ); } /** * Get parameter from view group * * @param unknown_type $param * @param unknown_type $value * @return unknown view's value if setted or JRequest' value */ static function getRequestState( $param, $default = null, $type = null ) { global $app; $jinput = new Jinput(); $view = $jinput->getString('view'); if ( !empty( $view ) ) $view .= "."; return $app->getUserStateFromRequest( $view . $param, $param, $default, $type ); } /** * Restores parameters(s) from Request view. group * * @param array $params edf filters if null */ function restoreRequestState( &$o, $params = null ) { global $app; $_REQUEST = $o->states->request; $jinput = new Jinput(); $view = $jinput->get('view'); $params = edfFilters::getValues( $view ); if ( !empty( $view ) ) $view .= "."; if (isset( $params ) ) foreach ( $params as $param=>$d ){ if ( isset( $o->states->registry->$param )) $value = $o->states->registry->$param; else $value = null; if ( is_string( $view. $param) ) $app->setUserState( $view . $param, $value ); } }

Avatar du membre
Mammouth du PHP | 979 Messages

15 août 2019, 15:23

J'ai retesté avec des objets d'environ 50ko et à priori non le poids de l'objet ne fera pas de différence, que ce soit par référence ou sans je ne pense pas que l'objet soit dupliqué dans la mémoire. Il s'agirait d'un tableau ça serait surement différent mais avec un objet à priori non. Je pense même en faite qu'à moins justement d'avoir des traitements qui sont appelés des dizaines de milliers de fois lors de l'exécution, les différences entre les 3 méthodes resteront anecdotiques car la méthode d'appel du traitement n'est qu'un petit maillon dans la chaine de tous les traitements. L'appel n'est qu'une part infime de tous les traitements. En l'occurrence ça se joue pour 1 appel sur du centième de milliseconde, traitement compris et encore je dis ça avec une machine qui doit dater de 2010 et qui n'était déjà pas à l'époque une machine de compétition.
La où tu pourrais avoir de réels gains de performance c'est si tu fais des traitements avec plusieurs lignes de code php alors qu'une fonction php existante permettrait de réaliser le même traitement. C'est en ne refaisant pas un traitement déjà réalisé, stocker et retourner le résultat. Et si y a de la base de données, il peut y avoir là aussi des méthodes pour gagner en performance, par exemple mieux vaut une requête qui retourne 100 lignes que 100 requêtes qui retournent 1 ligne, l'indexation des tables selon les filtres de requêtes, les jointures, etc.

Sinon je pense que l'utilisation de traits serait pertinente à la vue des traitements, mais bon je dis ça avec une vision assez superficielle de l'architecture du programme. ^^
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^