Newsgroup: it.comp.www.php
Titolo: creare un hash di qualunque valore

md5() crea l'hash di una stringa.
Sarebbe possibile creare anche un hash di un'istanza di una classe, di 
un array, ecc.?


Risposta corretta ma molto probabilmente non quello che ti serve:
Prima serializzi l'oggetto, poi ti prendi md5.
$hash = md5(serialize($obj));
Dico che probabilmente non è quello che ti serve perché due classi diverse
possono avere lo stesso hash.
 
Domanda: ma che devi fare??
 
Ciao!


controllare se un elemento di un array contiene già lo stesso valore 
(stringa, numero, array, oggetto, bool) che si sta per aggiungere 
(tramite array_push() oppure $array[]=$valore)
 


cioè in_array()? (o anche un semplice for?)
 
Se sei preoccupato per le performances perché ne devi fare tanti (e sei
sicuro di stare usando l'algoritmo giusto), puoi usare una hashmap, che in
PHP è.. l'array: inserisci i tuoi dati come chiavi invece che come valori
e controlli con isset().
 
O magari puoi semplicemente mettere dentro tutto e chiamare array_unique()
alla fine.
 
Per la migliore soluzione servono altri dettagli, ma di sicuro con questa cosa
degli hash stai andando fuori strada..
 
Ciao!

 possono avere lo stesso hash.
 
come è possibile?


A parte che con gli md5 puoi avere collisioni, e per quanto improbabile sia
non lo rende un metodo viabile.
 
Detto questo, basta avere due istanze dello stesso oggetto con gli stessi
valori per membri.
class A {
    private $i;
    public function __construct($i) { $this->i = $i; }
}
$a0 = new A(2);
$a1 = new A(2);
var_dump(md5(serialize($a0))==md5(serialize($a1))); //true
 
In ogni caso continui a non dire cosa vuoi risolvere. Così non si va molto
lontano.
 
Ciao!

 degli hash stai andando fuori strada..
 
class a {
	private $abc;
 
     function __construct($abc) {
         $this->abc=$abc;
     }
}
 
$arr=array();
array_push($arr,1);//ok
array_push($arr,1);//duplicato
array_push($arr,new a(1));//ok
array_push($arr,new a(2));//ok
array_push($arr,new a(2));//duplicato
 
Per impedire l'aggiunta dei duplicati?
 

 lontano.
 
leggi l'altro post, ma come già detto, forse ho trovato la soluzione
 

     throw new Exception('valore già esistente');
 
...naturalmente da inserire in un ciclo for

 Per impedire l'aggiunta dei duplicati?
 
Se due istanze differenti per te sono uguali se hanno gli stessi membri,
allora basta che fai:
 
$arr = array();
$arr[serialize($obj0)] = $obj0;
$arr[serialize($obj1)] = $obj1;
etc.
 
Non serve controllare perché tanto per te due oggetti uguali sono uguali,
che t'importa qual'è?
 
(non prendo in considerazione l'array con valori misti int/stringhe/oggetti
per ovvi motivi)
 
Ciao!


In ogni caso una soluzione più "normale" sarebbe indicizzare la tua classe
secondo qualche parametro, visto che non ti serve tutta la serializzazione in
ogni caso, magari usando un metodo (o __toString() se sei pigro) per avere
l'indice.
 
In una situazione ancora più normale, quello che chiedi non succede di farlo
praticamente mai, ma visto fai il misterioso chissà.
 
Ciao!

 che t'importa qual'è?
 
è sempre bene capire se, un valore è uguale (è già stato aggiunto), e 
quindi perchè è stato, o meglio, sta per essere aggiunto una seconda volta.
Lasciare che il tutto avvenga in silenzio (almeno un warning ci vuole) 
non è una cosa buona


Se sapessi di cosa stiamo parlando ti potrei forse dare ragione o torto, così
boh, lo saprai te che devi fare.
 
Ciao!

 boh, lo saprai te che devi fare.
 
class Collezione{
	static $arr=array();
 
	static function add($value){
		array_push(self::$arr, $value);
	}
}
 
Collezione::add(123);
 
poi da qualche altra parte del proggetto
 
Collezione::add(123);
 
poi da qualche altra parte del proggetto
 
Collezione::add(123);
 
poi da qualche altra parte del proggetto
 
Collezione::add(123);
 
poi da qualche altra parte del proggetto
 
Collezione::add(123);
 
poi da qualche altra parte del proggetto
 
Collezione::add(123);
 
A questo punto viene il dubbio... serve d'avvero aggiungere per 6-7 
volte sempre lo stesso valore?
Forse no...
Forse, per distrazione, si stanno facendo degli adding inutili...
... che tra l'altro appesantiscono l'applicazione.
Quindi non è il caso di implementare qualche controllo, in modo che il 
programmatore si accorga che c'è un po' di sporcizia (adding inutili), e 
quindi fare un po' di pulizia?


Oh, manco con le pinze, eh? Non eri te che dicevi che bisognava chiedere,
chiedere, chiedere.. ? :D Così però esageri!
 
Da come l'hai posta boh, non si può sapere. Quanti inserimenti sono in totale?
Cosa si sta inserendo? Che succede se ci sono duplicati? I duplicati vanno
contati, scartati o mandano tutto in eccezione? Cosa sono tutte queste
parti che fanno add ognuna per conto suo? Cos'è 'sta collezione? Perché è
statica?
Ma soprattutto: cosa-cacchio-rappresentano-sti-dati?
 
Non puoi fare una domanda di modellazione software senza dire cosa vuoi
modellare.
 
Ciao!

 quindi fare un po' di pulizia?
 
In generale no.
 
Se il programmatore sta aggiungendo sempre la stessa cosa all'array o sa 
quello che fa (che sarebbe il suo lavoro) o ha sbagliato architettura.
 
Non puoi imporre a un programmatore l'architettura generale della sua 
applicazione.
 
Soprattutto quando la tua stessa libreria sta ignorando completamente le 
regole basilari della programmazione, ma spero fosse solo un esempio per 
fare presto.
 
Bye.


 
guarda che sono già arrabbiato, quindi stai attento :D
 
 


Oh, ora è più chiaro! Non c'è bisogno che t'arrabbi, riconoscerai che questa
situazione c'entra ben poco con quello che avevi chiesto all'inizio, non ti
pare?
Comunque, questo è un caso più che comune: un'applicazione che prende la
configurazione da un file utente, altrimenti gira con i valori di default.
 
Come prima cosa l'array della configurazione ha sulle chiavi solo stringhe,
per cui non capisco perché cercavi un modo di serializzare degli oggetti.
Oltretutto non devi avvertire l'utente: se mette un valore nella sua
configurazione è perché vuole sovrascrivere il valore di default, è il
comportamento atteso.
 
A meno che non devi fare operazioni complesse sul parsing, e visto che l'utente
scrive in yaml, basta che scrivi a tua volta i valori di default in un file
yaml, e poi unisci gli array sovrascrivendo il default con i valori utenti,
se presenti.
 
$defaultConfiguration = yaml_parse_file("default.yaml");
$userConfiguration = yaml_parse_file("user.yaml");
$configuration = array_merge($defaultConfiguration, $userConfiguration);
 
Così è come fanno la quasi totalità delle applicazioni, e quindi è quello
che un utente credo s'aspetti.
 
Ciao!


 
non sto parlando di imporre architetture, ma implementare un minimo di 
checking per avvertirlo che sta facendo qualcosa che in futuro potrebbe 
crearli problemi...
È reato?
 

 per cui non capisco perché cercavi un modo di serializzare degli oggetti.
 
veramente parlavo di *dati* in generale...
Ok, ho tirato fuori il discorso della configurazione per fare un 
esempio, non molto azzeccato in effetti :P

 meglio preferire classi con metodi statici
 
O hai letto male o hai capito male o ti hanno detto male.
Non è quasi mai preferibile usare classi con metodi statici.
 
In questo caso può essere tollerabile, ma allora chiamala almeno 
Configurazione, non Collezione, e usa indici.
 
Bye.
 

 È reato?
 
Non è reato. È pura utopia.
 
YAGNI. Non puoi prevedere il futuro. Programma per il presente. Per il 
futuro c'è il refactoring (che è molto difficile se usi classi statiche).
 
Bye.
 

 
 
Allora: seguimi perché probabilmente questo sarà un post lungo :)
Cerco di tenerlo il più corto possibile, ma probabilmente sarà comunque una
bella sleppa di testo..
 
Prima di tutto, sto scrivendo male quello che puoi trovare in un sacco di
libri storici che *devi* leggere se vuoi fare questo lavoro. Primo fra tutti
uno che già t'ho citato qualche settimana fa, ma ti posto direttamente un link
per prendertelo da amazon: 
http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented-ebook/dp/B000SEIBB8
 
Puoi chiedere qui o su qualsiasi altro NG di programmazione, stai sicuro che
tutti ne hanno una copia a casa.
 
Vediamo:
 
Uno, nello specifico, non avrei usato niente, visto che alla fine di tutta la
pantomima devi solo fare una mkdir: avrei chiamato quella e basta, senza
alcun tipo di classe. Non c'è motivo di astrarre una funzione che è garantita
essere parte del core.
 
Due, sulla cosa static o no. Il concetto è facile (il che non vuol dire che
sia facile capire quando applicarlo o no, ma questa è un'altra storia):
se una classe non mantiene alcuno stato allora può essere statica.
Che vuol dire avere uno stato? Significa che i suoi metodi, quando richiamati,
usano, oltre ad i parametri passati, anche "altre cose", memorizzate da
qualche parte.
Esempi:
- l'array con la configurazione è chiaramente un concetto a se stante, e
decidi di avere una classe che la gestisce. Vuoi un metodo "prendi()" che
ritorna la configurazione e un "aggiungiValore(k,v)" che prende una coppia
chiave/valore e l'aggiunge alla configurazione. Chiaramente questa classe ha
uno stato: la configurazione attuale.
- ogni volta che crei un file o una cartella vuoi fare log su file o su
database: decidi di usare una classe con due metodi "creaFile" e "creaCartella"
per gestire la cosa. Questi due metodi non hanno bisogno di nessun'altra
informazione se non il nome del file o della cartella, per cui possono essere
statici. (o magari il nome del file su cui fare log è uno stato? :) esercizio
per il lettore).
 
Tre, mettiamo il caso che avevi classi non statiche (per l'esempio che hai
postato, immaginiamo che la classe che creava le directory lo faceva solo
sulla scrittura di un file, altrimenti, se nessuno ci metteva files dentro,
rimaneva una directory "virtuale" e non sarebbe stata creata per nulla - so che
l'esempio è stiracchiato, ma seguimi). In questo caso, a livello
architetturale, cosa volevi? Volevi un'instanza di DirectoryMaker unica per
ogni tipo di parametro passato al costruttore: un nome di cartella virtuale.
C'è un pattern famoso per questa situazione (o meglio, è famoso perché c'è un
"errore architetturale" famoso che vien fuori da situazioni simili che spingono
l'utilizzo di singleton, ma sto divagando): il factory. L'avrai sentito,
immagino ;) Fai una classe che si occupa di costruire le classi che gestiscono
le directory virtuali e, in questo caso, che fanno cache delle istanze già
esistenti che possono già fare il lavoro richiesto.
So che può sembrare complesso, ma in pratica è abbastanza banale, basta che fai
due prove e te ne rendi conto subito da solo.
 
Mi rendo conto che magari ho buttato troppa carne al fuoco tutt'insieme, per
questo spero che tu ti legga, oltre al il libro che ti ho suggerito (insisto
perché, sul serio, personalmente non son mai riuscito a concepire un
programmatore che non conosca le basi dei pattern più comuni), molte più cose
sulla modellazione software.
 
Ciao!


 
come sopra; poi dipende dai punti di vista
 
 

 dove devi scrivere procedurale."
 
E tu, di tutto il discorso, hai letto solo "solo con metodi statici"?
 
La prima riga dice "Per ora, se sei in-a-hurry". Che, tradotto, 
significa: se non hai tempo per fare una cosa fatta bene ma hai bisogno 
di risultati subito, inizia con una cosa fatta male, ma almeno dalle una 
struttura decente.
 
Ma implica che poi, più avanti, avresti imparato come farla bene.
 
Quello che ti ha suggerito in quel messaggio è che, piuttosto che usare 
funzioni globali per fare progetti grossi, è meglio almeno raggrupparle 
in classi con metodi statici.
 
Non certo che devi sempre usare metodi statici, anzi.
 
Bye.

 Non certo che devi sempre usare metodi statici, anzi.
 
certamente, ma è altrettanto consigliato *non* istanziare le classi 
quando *non* serve.
 
class abc{
    static function sum($a, $b) { return $a+$b; }
}
 
In questo caso, secondo te è proprio indispensabile istanziare?

 sulla modellazione software.
 
ok!!!
 

 futuro c'è il refactoring (che è molto difficile se usi classi statiche).
 
beh il refactoring è quasi un'operazione di routine
 
 

 In questo caso, secondo te è proprio indispensabile istanziare?
 
Questo è un caso limite, ma in generale dipende.
 
Se domani vuoi usare una classe diversa ma con la stessa funzione?
 
Per esempio, una che supporta la somma tra numeri immaginari. Ti tocca 
cercare tutte le chiamate statiche nel tuo codice e cambiarle con la 
chiamata alla nuova classe.
 
Anche il mio è un esempio estremo, mi rendo conto, e viola parzialmente 
lo YAGNI (ma è più SOLID), ma con questo tipo di esempi è difficile fare 
controesempi.
 
Bye.


 
????
Chiedo scusa, ma avrai già intuito che a volte faccio fatica a seguirti.
Certo tu (ma anche gli altri) mi stai facendo notare delle cose molto 
interessanti, ma se non cerchi di esprimerti con un po' di chiarezza, 
non riesco a centrare pienamente il concetto
 
 

 non riesco a centrare pienamente il concetto
 
bootstrap.php
 
$app = new App();
$app->set('somma', new Sommante());
 
$qualcosa = new Qualcosa($app);
$somma = $qualcosa->fai(10, 20);
...
 
Qualcosa.php (e altri 1000 file):
 
class Qualcosa {
  public function __construct(App $app)
  {
    $this->app = $app;
  }
 
  public function fai($a, $b) {
    $res = $this->app->get('somma')->sum($a, $b);
  }
}
 
 
Un domani...
 
class SommanteImmaginario {
  public function sum($a, $b) {
    if ($a instanceof ImgNumber && $b instanceof ImgNumber) {
      // fai la somma tra immaginari
    } else {
      return $a + $b
    }
  }
}
 
modifiche al tuo codice: 1, in bootstrap.php
 
$app->set('somma', new SommanteImmaginario());
 
Questo si chiama Dependency Injection (DI). Inietti un oggetto di classe 
App in altre classi, e lui fa da Dependency Injection Container (DIC) 
tenendo una qualsivoglia copia di Sommante e fornendola a chi la richiede.
 
 
Ma spingiamoci oltre:
 
interface Sommabile {
  public function sum($a, $b);
}
 
class Sommante implements Sommabile { ... }
 
class SommanteImmaginario implements Sommabile {
  public $orig = null;
  public function __construct(Sommabile $originale) {
    $this->orig = $originale;
  }
 
  public function sum($a, $b) {
    if ($a instanceof ImgNumber && $b instanceof ImgNumber) {
      // fai la somma tra immaginari
    } else {
      return $this->orig->sum($a, $b);
    }
  }
}
 
Il tuo bootstrap diventerà:
 
$app->set('somma', new SommanteImmaginario(new Sommante()));
 
Questo si chiama Decoratore, e si usa solitamente con la DI di cui sopra. 
Programmi a contratto (l'interface Sommabile, che garantisce di avere una 
funzione sum che prende due parametri). A questo punto che tu passi 
Sommante o SommanteImmaginario non fa differenza, perché poi lui si 
aspetta un Sommabile. SommanteImmaginario è un Sommabile che accetta un 
altro Sommabile da wrappare/decorare. Puoi metterne quanti ne vuoi nel 
bootstrap, e il resto del codice (se rispetta il contratto) non avrà 
nessun problema a gestirlo.
 
Queste cose semplicemente non le puoi fare con le classi statiche.
 
Puoi avere la tentazione di usare App come statica, e per progetti medio-
piccoli ci sta e lo faccio anche io. Difficilmente cambierà la struttura 
portante dell'applicazione, e se cambia faccio prima a riscrivere 
l'applicazione che a fare refactoring di tutto.
 
Ma, per fare l'esempio del tuo configuratore, puoi usare una classe base 
con getter e setter da usare nel bootstrap, e poi decorarla con una che 
legge anche da un file YAML, a sua volta decorata da un'altra che fa 
l'override dei valori prendendoli da DB, a sua volta decorata da un'altra 
che fa caching di tutto l'ambaradan su memcache per non dover fare il 
parsing ad ogni connessione.
 
Avresti
 
interface ConfigurationManager {
 public function set($k, $v);
 public function get($k);
}
 
class BasicConfig implements ConfigurationManager {
 protected $config = array();
 public function set($k, $v) { $this->config[$k] = $v; }
 public function get($k) { return (@$this->config[$k] ?: null); }
}
 
class YAMLConfig implements ConfigurationManager {
  protected $wrapped, $yamlFile, $parsed = false;
  public function __construct($yamlFile, $wrapped) {
    $this->wrapped = $wrapped;
    $this->yamlFile = $yamlFile;
  }
  public function get($k) { 
    if (!$this->parsed) { $this->parseYaml(); }
    $this->wrapped->get($k); 
  }
  public function set($k, $v) { 
    if (!$this->parsed) { $this->parseYaml(); }
    $this->wrapped->set($k, $v); 
  }
  public function parseYaml($yamlFile) { ...; $this->parsed = true; }
}
 
class DBConfig implements ConfigurationManager {
  protected $pdo, $wrapped, $parsed = false, $tableName;
  public function __construct($pdo, $tableName, $wrapped) {
    $this->wrapped = $wrapped;
    $this->pdo = $pdo;
    $this->tableName = $tableName;
  }
  public function get($k) { 
    if (!$this->parsed) { $this->parseDb(); }
    $this->wrapped->get($k); }
  }
  public function set($k, $v) { 
    if (!$this->parsed) { $this->parseDb(); }
    $this->wrapped->set($k, $v); }
  }
  public function parseDb() { ... }
}
 
class MemcachedConfig implements ConfigurationManager {
  protected $memcache, $ttl, $wrapped;
  public function __construct($memcache, $ttl, $wrapped) {
    $this->wrapped = $wrapped;
    $this->memcache = $memcache;
    $this->ttl = $ttl;
  }
  public function get($k) {
    if (time() < $ttl) {
      return $this->getFromMemcache($k);
    } else {
      return $this->wrapped->get($k);
    }
  }
  public function set($k, $v) {
    $this->ttl = 0;
    $this->wrapped->set($k, $v);
    $this->saveAllToMemcache();
  }
  public function getFromMemcache($k) { ... }
  public function saveAllToMemcache() { ... }
}
 
E, finalmente, nel bootstrap:
 
$config = 
new MemcachedConfig(
  new Memcached('blablabla'),
  5*60,
  new DbConfig(
    new PDO('sqlite:/tmp/config.db'),
    'configuration',
    new YamlConfig(
      '/etc/myapp.config.yaml',
      new Config()
    )
  )
)
;
 
Vuoi testare senza memcache? Basta commentare le righe 2, 3, 4 a la 
penultima (o scrivere il codice senza Piramidi di Paura e commentare solo 
una riga).
Vuoi togliere il DB? Commenti la riga che decora con DbConfig.
Ecc. ecc.
 
Per questo ti dicevo che trovo il tuo controllo abbastanza inutile. Una 
classe deve fare il minimo indispensabile.
 
Vuoi aggiungere il controllo? Fai un decoratore che, al set(), verifica 
prima con un get() se il valore c'è già ed eventualmente lancia 
un'eccezione.
 
Bye.
 
PS.: Ecco, oggi mi stavo decidento a scrivere un articolo sul blog e me 
l'hai fatto scrivere qui. :P

 un'eccezione.
 
qualcosa tipo questa?
 
$value=123;
$config->set(new UnivocalValueChecker($config, $value));


No, tipo questa:
 
class Config
{
 public $values = array();
 public function set($v) { $this->values[] = $v; }
}
 
class UnivocalConfig
{
  protected $wrapped;
  public function __construct($wrapped) {
    $this->wrapped = $wrapped;
  }
  public function set($v) {
    foreach ($this->wrapped->values as $value) {
      if ($value == $v) {
        throw new Exception('Cannot have duplicates');
      }
    }
    $this->wrapped->set($v);
  }
}
 
$config = new UnivocalConfig(new Config());
 
$config->set('pippo');
$config->set('pippo'); // -> Exception
 
 
Bye.
 

 l'hai fatto scrivere qui. :P
 
+10 solo perché sei riuscito (IMHO) a spiegare il tuo punto con il solo codice.
Non che personalmente farei tutto quest'uso di decorators, ma quando una
cosa è ben fatta fa piacere agli occhi leggerla.
 
Ciao!

 Non che personalmente farei tutto quest'uso di decorators
 
Ma nemmeno io. :D
 
Al massimo finora mi è capitato di usare uno o due decoratori.
L'esempio era un po' estremizzato per far capire cosa ci si poteva fare.
 
Bye.

 $config->set('pippo'); // -> Exception
 
grazie