Newsgroup: it.comp.www.php
Titolo: [?] Test OOP - classi derivate e __get

publicFields)) 
				throw new \InvalidArgumentException("La 
property '{$property}' non e' accessibile!");
		    	$ris = $this->$property;
			return $ris;
	    	} //__get
		public function __construct() {
		} //costruttore
	} //class GenericClass
 
Semplice classe derivata:
 
class DatiBrowser extends GenericClass {
	private $language;
	public function __construct() {
		$this->publicFields=array("language"=>"it");
		$this->language="it";
	}
}
 
istanzio:
	$b=new DatiBrowser();
	$leggo=$b->language;
 
Errore: 
 
Notice: Undefined property: DatiBrowser::$language in ... genericClass.php
 
Non capisco:
 
a) __get viene "ereditata", e quindi dovrebbe sapere cosa è $language ... 
o no?
b) perché mai property_exists non segnala errore (quindi trova la property 
settata nella derivata), ma subito dopo $this->$property segnala l'errore 
sopra indicato (e sembra quindi non trovare la property che ha trovato 
subito prima)?
 
Scusate ma sto capendo come è implementato OOP in php5 e sono un po' 
confuso ... la mia intenzione era di avere una classe base con un __get 
generico, e usarlo poi in tutte le derivate.
 
php. 5.3.5
 
Grazie
Cristiano

 Non capisco:
 
__get viene richiamata SOLO se la property non esiste nell'oggetto.
 
Nel tuo caso la property "language" esiste, ma e` private, quindi non 
viene richiamata __get, ma viene acceduta direttamente la property, che 
essendo private torna errore.
 
Ho quotato il if(!property_exists()) per farti notare che non serve a 
nulla messo li`, in quanto appunto se la property esiste non arrivi a 
quel pezzo di codice.
 
Bye.
 

 quel pezzo di codice.
 
Sei sicuro di quello che dici?
 
Un esempio completo e funzionante, basta un copiaincolla in un file php e 
dovrebbe funzionare:
 
<?php
class GenericClass {
	protected $publicFields = array();
	 public function __get($property) {
	 	//esiste la property?
		echo "Verifica esistenza property ...";
		if(!property_exists($this,$property))
	            throw new \InvalidArgumentException("La property 
'{$property}' non esiste!");
		echo "ok<br/>";
		//è accessibile?
		echo "Verifica esistenza nell'array...";
		if (!array_key_exists($property, $this->publicFields))
			throw new \InvalidArgumentException("La property 
'{$property}' non e' accessibile!");
		echo "ok<br/>";
		echo "Leggo la property...";
	    	$ris = $this->$property;
		echo "ok<br/>";
		return $ris;
    	} //__get
	public function __construct() {
	} //costruttore
} //class GenericClass
class DatiBrowser extends GenericClass {
	private $language;
	public function __construct() {
		$this->publicFields=array("language"=>"it"); 
		$this->language="it";
	}
}
$b=new DatiBrowser();
$leggo=$b->language;
?>
 
L'output che ottengo è:
 
Verifica esistenza property ...ok
Verifica esistenza nell'array...ok
Leggo la property...
Notice: Undefined property: DatiBrowser::$language in /opt/lampp/htdocs/
test/index.php on line 16
ok
 
Quindi sì che ci passa da __get della classe base ...

Il Sat, 05 Nov 2011 06:51:30 +0000, Cristiano Larghi ha scritto:
 
Risolto partendo da questo post:
 
http://forums.devshed.com/php-development-5/inheriting-get-var-
 
method-606971.html
 
Praticamente la classe base non può accedere ai fields delle derivate se 
sono dichiarati private ... cambiato da private a protected, ora l'output 
è corretto.
 
E' però IMHO assurdo (e, sempre IMHO, contrario alla logica OOP), e c'è 
una incongruenza: indica che la property esiste, ma poi segnala 
"undefined" ...

 "undefined" ...
 
Finché lo dichiarano non è incongruente:
"Changelog
Version  Description
5.3.0    This function checks the existence of a property independent of 
accessibility."


Beh, l'incongruenza "logica" con la logica della programmazione OOP è 
indipendente dall'implementazione ... o no?
 
Comunque sia ci sta (posso verificare che il field esista ma non leggerne 
il valore), anche se non capisco il perché; quello che più mi lascia 
perplesso è che un metodo (function) di una classe base non possa 
accedere ai fields private di una classe derivata, questo è IMHO assurdo 
e contrario a ogni logica OOP, visto che la derivata è una "estensione" 
della classe base.

 :
 
~ $ cat foo.php 
<?php
class GenericClass {
   protected $publicFields = array();
    public function __get($property) {
      //esiste la property?
      echo "Verifica esistenza property ...";
      if(!property_exists($this,$property))
               throw new \InvalidArgumentException("La property 
'{$property}' non esiste!");
      echo "ok\n";
      //è accessibile?
      echo "Verifica esistenza nell'array...";
      if (!array_key_exists($property, $this->publicFields))
         throw new \InvalidArgumentException("La property 
'{$property}' non e' accessibile!");
      echo "ok\n";
      echo "Leggo la property...";
         $ris = $this->$property;
      echo "ok\n";
      return $ris;
      } //__get
   public function __construct() {
   } //costruttore
} //class GenericClass
class DatiBrowser extends GenericClass {
   private $language;
   public function __construct() {
      $this->publicFields=array("language"=>"it"); 
      $this->language="it";
   }
}
$b=new DatiBrowser();
$leggo=$b->language;
?>
~ $ php foo.php 
Verifica esistenza property ...ok
Verifica esistenza nell'array...ok
Leggo la property...ok
~ $

 ~ $
 
Mi stai dicendo che a te funziona mantenendo il private? versione di php?

 ~ $
 


 Mi stai dicendo che a te funziona mantenendo il private? versione di php?
 
~ $ php --version
PHP 5.3.6 with Suhosin-Patch (cli) (built: Sep  8 2011 19:34:00) 
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

 1998-2011 Zend Technologies
 
Stranissimo. Devo capire come aggiornare php/xampp per testare con 
l'ultima versione stable.
 
Grazie.

 l'ultima versione stable.
 
Testato con php 5.3.8 e confermo il mio risultato (se lascio private 
segnala "undefined property", se cambio in protected tutto ok).

 segnala "undefined property", se cambio in protected tutto ok).
 
Provi da CLI, mod_php o che altro?

 Provi da CLI, mod_php o che altro?
 
Provo con una pagina php in localhost; php installato automaticamente da 
wampp senza alcuna modifica da parte mia.
 
phpinfo() mi indica mod_php5 tra i loaded modules.


"Members declared as private may only be accessed by the class that 
defines the member."
 
Comportamento analogo lo hai anche in linguaggi meno "dinamici" (come 
java), anche se si nota piu' spesso quando la classi *derivate* provano 
ad accedere a membri provati della base.

 ad accedere a membri provati della base.
 
ok, come dicevo: ci sta.
Ma come mai a Andrea D'Amore invece funziona?


Per curiosita' ho appena provato anche da me e funziona!... o forse no :P
Nel senso che non ho errori a video (provato CLI) ma se aggiungo un
var_dump($leggo);
ottengo il buon vecchio NULL, come + o - ci si aspettava. Credo che sia 
semplicemente un differente livello di verbosita' negli errori...

 semplicemente un differente livello di verbosita' negli errori...
 
:-O
Non ci avevo pensato. Grazie, ora è chiaro.

 errori...
 
E la bambolina di Rasmus Lerdorf va al signore. Anche io non ho errore 
ma non accedo al valore della variabile protected.

 	    	$ris = $this->$property;
 
Mentre qui cerchi di leggere $language come property dell'oggetto, e non 
come elemento dell'array.
 
Tenendo presente che __get viene chiamata solo per property inesistenti o 
private, puoi modificare la funzione cosi`:
 
public function __get($property) {
  if (!array_key_exists($property, $this->publicFields))
    throw new \InvalidArgumentException("La property '{$property}' non e' 
accessibile!");
 
  return $this->publicFields[$property];
} //__get
 
 
Bye.
 

 } //__get
 
Verifico prima l'esistenza perché array_key_exists è "manuale" (settato 
dallo sviluppatore), e potrebbe sbagliare a scrivere il nome della 
proprietà.
Per questo motivo prima verifico che esista, e poi che chi ha creato la 
classe abbia specificato che il getter debba restituirne il valore.
 
Se tolgo la verifica dell'esistenza, se scrivo per errore nell'array il 
nome di una proprietà non esistente avrò un errore nel return.

 nome di una proprietà non esistente avrò un errore nel return.
 
Credo che tu stia seguendo la strada sbagliata.
 
Prova a spiegare cosa vorresti ottenere (a livello di flusso operativo, 
non come codice) e vediamo se ci sono modi migliori per farlo.
 
Bye.

 non come codice) e vediamo se ci sono modi migliori per farlo.
 
Voglio una classe base che gestisca un getter di base, senza ogni volta 
dover implementare la __get. Tutte le mie classi derivano da questa 
classe base.
 
In questo modo posso dichiarare "public" (tramite l'array) le properties 
che voglio, e se è il caso gestire la __get solo per i getter che hanno 
bisogno di elaborazioni per restituire il risultato (per gli altri 
richiamerò comunque il metodo-base, che si limiterà a restituire il 
valore tale quale).
 
L'idea è:
 - classe base che verifica che:
	- la property richiesta esista
	- sia dichiarata accessibile all'esterno
	- ne restituisca il valore
 
 - classe derivata:
	- Indica nell'array quali property private (ora protected per 
quanto emerso da questo thread) sono da considerarsi public
	- tutto il resto lo lascia alla classe base, tranne per le 
properties che abbisogna di un getter apposito; in tal caso override del 
metodo base, restituisco il valore "particolare" e richiamo la base per 
tutti gli altri.

 valore tale quale).
 
Puoi fare una cosa del genere:
 
class Base {
 private $_allowed = array();
 private $_data = array();
 
 protected function allowProperty($k)
 {
  $this->_allowed[$k] = true;
 }
 
 public function __get($k) {
  if (array_key_exists($this->_allowed, $k) && $this->_allowed[$k]==true)
  {
   return $this->_data[$k];
  } else throw ...
 }
 
 public function __set($k, $v)
 {
  if (array_key_exists($this->_allowed, $k) && $this->_allowed[$k]==true)
  {
    $this->_data[$k] = $v;
  } else throw ...
 }
}
 
class Derivata extends Base
{
 public function __construct()
 {
  $this->allowProperty('language');
 }
}
 
 
NON TESTATA, scritta di getto. :) Sono da verificare quei due private, ma 
io non uso quasi mai private, quindi non ho mai studiato a fondo gli 
effetti.
 
Questo ti consente, per esempio, anche di implementare Iterator e 
ArrayAccess nella classe base, in seguito.
 
Bye.
 

 ArrayAccess nella classe base, in seguito.
 
Grazie, fino a qui non sono ancora arrivato, me li studio.

 sempre se ho ben capito).
 
Perche` tu vuoi dichiarare le "variabili extra" dei figli come private 
dei figli, ma non puo` funzionare.
 
Forse puoi fare qualcosa con il "nuovo static" di PHP 5.3, ma appunto 
diventano variabili statiche (di classe) e non dinamiche (di oggetto).
 
Se usi il codice che ti ho postato, puoi "creare" le variabili nel 
costrutture della classe, ed eventualmente sovrascrivere i magic accessor 
__get e __set per personalizzarli a livello di classe.
 
Bye.
 

 dei figli, ma non puo` funzionare.
 
Sì, questo mi è chiaro e ora le dichiarerò come protected. 
 
A parte questo, però, per quello che voglio ottenere mi sembra più 
funzionale il mio codice che non il tuo, per i motivi che ho esposto.