[PHP] unserialize in parent constructor, subclass onmogelijk

Pagina: 1
Acties:
  • 108 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • JayVee
  • Registratie: Mei 2002
  • Laatst online: 31-08 10:22

JayVee

shibby++!

Topicstarter
Argh, ben nu twee dagen bezig geweest een object anders op te bouwen. Eerst werd in de constructor alle data (die eigenlijk statisch is, maar per implementatie van het pakket wel verschilt) uit verschillende database tabellen gehaald. Nu werkt het door het object te serializen / unserializen.

Vandaag ging ik dan een subclass maken en loop ik tegen een gemeen probleem aan. Ik gebruik PHP4 maar ik denk dat ik met PHP5 hetzelfde probleem zou krijgen. Als ik in de subclass de constructor van de parent aanroep (dmv parent::ClassName), krijg ik een object van de parent class terug, niet van de subclass.

Een voorbeeld:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Super {
  function Super () {
    $this = unserialize( getStringFromDB() );    // haal een string uit db en unserialize object
  }
}

class Sub extends Super {
  function Sub () {
    parent::Super();
  }
}

echo get_type( new Sub );  // geeft Super, niet Sub


Uiteraard zijn alle strings in de database geserializde Super objecten, maar dat hadden jullie natuurlijk wel door! :Y)

Weet iemand een manier (of een goede hack) om dit alsnog voor elkaar te krijgen?

// edit:
Ik heb al een manier gevonden, maar erg netjes is die niet:
PHP:
1
2
3
4
5
6
class Super {
  function Super () {
    $tmp = unserialize( getStringFromDB() );
    $this->variable = $tmp->variable;
  }
}
Nu maak ik dus een temp object aan en kopieer ik alle variabelen. :X
Weet iemand een betere manier?

[ Voor 15% gewijzigd door JayVee op 27-07-2006 13:06 ]

ASCII stupid question, get a stupid ANSI!


Acties:
  • 0 Henk 'm!

Verwijderd

JayVee schreef op donderdag 27 juli 2006 @ 12:52:
Ik gebruik PHP4 maar ik denk dat ik met PHP5 hetzelfde probleem zou krijgen.
In PHP5 zou je dit in elk geval niet krijgen, want daar mag assignen aan $this niet :X.

En ik snap niet waarom je dit wilt -- als je een object van type Super serialiseert, dan hoor je ook een object van type Super terug te krijgen. Met Milton illes an orme dus.

Ik denk dat je ontwerp een beetje rammelt... leg eens uit wat je eigenlijk probeert te bereiken?

[ Voor 30% gewijzigd door Verwijderd op 27-07-2006 13:44 ]


Acties:
  • 0 Henk 'm!

  • remcotolsma
  • Registratie: December 2005
  • Laatst online: 08-09 11:11
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
class PObject 
{
  function PObject($object = NULL) 
  {
    if(isset($object))
    {
      $this = $object
    }
  }
}

$o1 = new PObject();
$o2 = new PObject($o1);

Bovenstaande code werkt niet in PHP5:
PHP:
1
Fatal error: Cannot re-assign $this in test.php on line 10


Volgens mij heeft je methode sowieso wel een aantal grote nadelen:
  • Je kunt de kracht van een DB niet optimaal gebruiken omdat alle object er als 1 lange string in staan
  • Mocht de classes in de toekomst worden uitgebreid / veranderd dan kunnen de oude strings in de database misschien wel niet meer correct ge'unserialize'd worden
Zo zijn er nog wel een aantal nadelen...

Net iets te laat :), OneOfBorg was me voor

Eventueel kun je hier iets mee:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function getStringFromDB()
{
  // haal iets uit de database...

  return serialize(new PObject('test 1', 'test 2', 'test 3'));
}

class PObject 
{
  private $var1;
  private $var2;
  private $var3;

  public function __construct($var1, $var2, $var3) 
  {
    $this->var1 = $var1;
    $this->var2 = $var2;
    $this->var3 = $var3;
  }
  
  public static function get()
  {
    return unserialize(getStringFromDB());
  }
}

$o1 = new PObject('var 1', 'var 2', 'var 3');
$o2 = PObject::get();

print_r($o1);
print_r($o2);

[ Voor 49% gewijzigd door remcotolsma op 27-07-2006 14:02 ]


Acties:
  • 0 Henk 'm!

  • JayVee
  • Registratie: Mei 2002
  • Laatst online: 31-08 10:22

JayVee

shibby++!

Topicstarter
Verwijderd schreef op donderdag 27 juli 2006 @ 13:42:
In PHP5 zou je dit in elk geval niet krijgen, want daar mag assignen aan $this niet :X.
A ha, dan moet ik het ontwerp zeker aanpassen, het moet natuurlijk wel compatible zijn met PHP5. Vond al vreemd (maar wel handig) dat het kon.
Met Milton illes an orme dus.
offtopic:
Say what? }:O Zelfs Google kan mij niet helpen hiermee! B)
Ik denk dat je ontwerp een beetje rammelt... leg eens uit wat je eigenlijk probeert te bereiken?
Het idee is het volgende:
we hebben een pakket ontwikkelt en dit object is vrij belangrijk en complex. Elke implementatie van het pakket kent een (vooraf vastgelegd) aantal objecten, die meestal statisch blijven als ze eenmaal voor de klant zijn ingericht. Nu zijn we de functionaliteit aan het ontwikkelen om deze objecten toch door de gebruiker te laten beheren.
Ik wil nu twee classen hebben: een read-only versie en een beheer versie (met set methoden om het object te veranderen) van de class. De beheer versie wordt niet altijd meegeleverd (verkopen we apart) en in 99.5% van de gevallen (pageviews) is alleen de read-only functionaliteit nodig. Vandaar dat ik het dus graag wil splitsen.Uit performance overweging kan je ook nog eens zeggen dat het jammer is om in 99.5% van de gevallen drie keer zo veel code te laten parsen door PHP. Daarnaast is het qua beheersbaarheid mooi om de code te splitsen in aparte classen.
remcotolsma schreef op donderdag 27 juli 2006 @ 13:44:
Volgens mij heeft je methode sowieso wel een aantal grote nadelen:
  • Je kunt de kracht van een DB niet optimaal gebruiken omdat alle object er als 1 lange string in staan
  • Mocht de classes in de toekomst worden uitgebreid / veranderd dan kunnen de oude strings in de database misschien wel niet meer correct ge'unserialize'd worden
Zo zijn er nog wel een aantal nadelen...
We hebben er uitgebreid en lang over nagedacht. De redenatie samengevat:
• Foreign key constraints kan je ook in software bijhouden, en aangezien er maar een stuk software is die deze checks moet doen (de rest van de software veranderd niets aan deze data) kan je het ook zelf doen. Als veel stukken code of zelfs andere applicaties de data aan moeten passen zou ik zeker bij foreign key constraints op database niveau blijven!
• Het (automatisch) backuppen en veranderen van de data (bij de klant!) is veel makkelijker op deze manier!
• Bij het uitbreiden van het object hoeven we nu alleen een tool te schrijven die de objecten 'update', en geen tool dat de database veranderd (nieuwe tabellen, etc). Dat hebben we nu al vaker gehad en kost altijd veel tijd (vooral het testen).

Conclusie: Topic komt (terecht) beetje n00by over omdat ik dit had moeten weten. Ik wil wel even aanmerken dat we wel degelijk hebben nagedacht over deze opzet!

En ja, ik zoek nog steeds de mooiste oplossing voor deze situatie. Kort samengevat:
Ik heb een class wat meestal read-only gebruikt wordt. De objecten wil ik zo serialized in de db zetten. Daarnaast wil ik een class die het object kan veranderen en weer serialized naar de db terug schrijft (en een backup maakt van het oude object en nog meer data). Deze tweede class vind ik semantisch gezien een mooi voorbeeld voor inheritance, en ik wil het dan ook graag zo opzetten.

Remco, je tweede voorbeeld vind ik een beetje te hacky. Vind je zelf niet ook?

ASCII stupid question, get a stupid ANSI!


Acties:
  • 0 Henk 'm!

Verwijderd

JayVee schreef op donderdag 27 juli 2006 @ 14:27:
offtopic:
Say what? }:O Zelfs Google kan mij niet helpen hiermee! B)
Sorry, beetje obscure referentie naar een Lucky Luke album :). Ik zag naderhand inderdaad ook dat het met Google niet te vinden was. In het kort komt het er op neer dat bij het telegraferen de telegrafist van de gevangenis de a met de i, en de m met de b verwisselt. Er staat dus eigenlijk: "Met Dalton alles in orde". In het begin van het verhaal vergiste de telegrafist zich ook met het bericht dat Joe Milton vrijgelaten moest worden uit de gevangenis... waardoor dus Joe Dalton werd vrijgelaten -- waardoor Lucky Luke er weer achter aan moest, je kent het wel. Waarop Lucky het volgende telegram naar de gevangenis stuurt: Amaoot! (Dat is met Google wel weer een paar keer te vinden).

*kuch* Beetje offtopic ja ;)
Ik heb een class wat meestal read-only gebruikt wordt. De objecten wil ik zo serialized in de db zetten. Daarnaast wil ik een class die het object kan veranderen en weer serialized naar de db terug schrijft (en een backup maakt van het oude object en nog meer data). Deze tweede class vind ik semantisch gezien een mooi voorbeeld voor inheritance, en ik wil het dan ook graag zo opzetten.
Ik vind het geheel nog een beetje raar... is het een end-user pakket, of wordt er door de klant nog tegenaan geprogrammeerd? Want als het het eerste is, wat boeit het dan of er ook nog setters op je class staan, zolang die door geen enkele pagina aangeroepen worden? En als het het tweede is en ze willen de configuratie veranderen, dan kunnen ze je source gewoon aanpassen (of op een andere manier in de db klooien). Dus de motivatie snap ik niet helemaal.

Makkelijkste oplossing: sla de gegevens op een associatieve array (instance variabele van de klasse), en serialiseer/onserialiseer die array, in plaats van de hele klasse. Dan kun je met je getters en setters in de array gaan zitten porren ipv op instance variabelen, en kun je je inheritance schema toepassen.

Als je perse instance variabelen wilt houden, dan kun je vast ook wel wat code schrijven om je instance variabelen eerst te compacten naar een array, die te serialiseren, en na het onserialiseren de elementen van de array weer terug te kopiëren naar de instance variabelen. En dat hoeft niet handmatig... met wat introspectie valt dat allemaal best te automatiseren.

Acties:
  • 0 Henk 'm!

  • JayVee
  • Registratie: Mei 2002
  • Laatst online: 31-08 10:22

JayVee

shibby++!

Topicstarter
Verwijderd schreef op donderdag 27 juli 2006 @ 16:57:
Sorry, beetje obscure referentie naar een Lucky Luke album :).
Nooit gelezen vroeger. Sorry! :D
Ik vind het geheel nog een beetje raar... is het een end-user pakket, of wordt er door de klant nog tegenaan geprogrammeerd? Want als het het eerste is, wat boeit het dan of er ook nog setters op je class staan, zolang die door geen enkele pagina aangeroepen worden? Dus de motivatie snap ik niet helemaal.
De eindklant krijgt geencrypte code. Daarnaast hebben onze klanten totaal geen verstand van PHP.

Ik wil de setters (en de backup-, integrity check- en meer functies!) van de getters scheiden om het wat netter te maken. Dat is alles.
• Anders krijg ik een class file van > 1 000 regels, dat werkt niet lekker
• Parser werkt niet efficient (de 'get class' wordt bijna altijd ingeladen, de set class misschien twee dagen per jaar! Erg miereneukerig, I know)
Makkelijkste oplossing: sla de gegevens op een associatieve array (instance variabele van de klasse), en serialiseer/onserialiseer die array, in plaats van de hele klasse. Dan kun je met je getters en setters in de array gaan zitten porren ipv op instance variabelen, en kun je je inheritance schema toepassen.
Dat is natuurlijk niet erg netjes OOP. Dat is wat ik voor de (get) class had, een grote associative array. Om het wat netter te maken (en help functies te kunnen gebruiken, zodat je niet altijd de documentatie van die reuze array hoeft te bekijken om iets te checken) en om de IDE te helpen met de auto complete feature heb ik er een object van gemaakt. En dat bevalt erg goed!
Als je perse instance variabelen wilt houden, dan kun je vast ook wel wat code schrijven om je instance variabelen eerst te compacten naar een array, die te serialiseren, en na het onserialiseren de elementen van de array weer terug te kopiëren naar de instance variabelen. En dat hoeft niet handmatig... met wat introspectie valt dat allemaal best te automatiseren.
Ik heb het nu zo gedaan als ik in de edit van mijn startpost vermeld:
PHP:
1
2
3
4
5
6
7
8
9
class Super {
  function Super () {
    $tmp = unserialize( getStringFromDB() );

    foreach ( array_keys(get_object_vars($tmp)) as $varName ) {
      $this->$varName = $tmp->$varName;
    }
  }
}

getStringFromDB is trouwens geen echte functie in mijn class maar kun je zien als abstractie voor de code die de string uit de db haalt.

ASCII stupid question, get a stupid ANSI!

Pagina: 1