PHP - passing variable from one object to another troubles - php

I've recently started to work with OO PHP. As a training practice I'm trying to write some simple classes. I have trouble passing a variable from one to another class. Is it even possible?
class group
{
public $array = array();
public function person($name,$surname)
{
$this->person = new person($name,$surname);
}
public function __destruct()
{
print_r($this->array);
}
}
class person
{
public function __construct($name,$surname)
{
$this->name = $name;
$this->surname = $surname;
}
}
$A = new group();
$A->person("John","Doe");
What I want to archieve here is to pass person as another member of group (by simply putting it in group array) for further modifications and sorting. Been googling around but found nothing.
Please forgive me if it's a dumb one. ;)

I'm not sure I totally understand but I think you want:
Class group {
public $members=array();
public function person($name,$surname) {
$this->members[]=new person($name,$surname);
//Creates a new person object and adds it to the internal array.
}
/*...*/
}
A better alternative (seperation of intent) would be:
Class group {
public $members=array();
public function addPerson(person $p) {
$this->members[]=$p;
//Avoids this function need to know how to construct a person object
// which means you can change the constructor, or add other properties
// to the person object before passing it to this group.
}
/*...*/
}

The fix is changing
public function person($name,$surname)
{
$this->person = new person($name,$surname);
}
to
public function person($name,$surname)
{
$this->array[] = new person($name,$surname);
}
$this->person is not being stored in the array otherwise, and is overwritten with each call.
Your group class could improve it's OO by:
changing $array to be more descriptively named
changing the function name person to something more meaningful, like add_person

You should define your properties ('name', 'surname') and give them a suitability visibility
class group
{
public $array = array();
public name;
public surname;
...
Reference: http://php.net/manual/en/language.oop5.visibility.php

Related

Is there a way to make __get & __set trigger for defined but empty properties in class

I´ve tried to understand if there is a magic function in php that can trigger a function when I try to access an object property like the following.
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
}
function __get($name){
// Trigger on defined property such as groups
if($name == "groups"){
$this->loadGroups();
return $this->groups;
}
}
}
$user = new User();
foreach($user->groups as $group) // Is it possible to load $user->groups when it's accessed?
echo $group;
I do know that the __get & __set does not trigger if the property is defined in the class even tho the property is set to null or undefined, but is there any other way to trigger something for a defined property or do I have to create getters and setters for all these properties and make sure to always call these when i need to access the property and want to be sure that it's loaded when i access it? I do hope not due to It will result in many changes for the current system I'm working with.
I'm thankful for any information or help I can get to close this chapter.
// ZarToK
I get what you're trying to do, but I don't think you're asking the question right. You're obviously trying to do lazy loading.
There isn't a magic method for doing what you're requesting, but you can essentially create such a thing. There are two options: the "creating getters/setters" option - which is more in line with what a Java developer would do, or there's the "protect the variable and use magic method" option which tends to be "nice" for framework apis but can be a little confusing.
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function __get($name) {
if ($name == 'groups' && is_null($this->groups)) {
$this->loadGroups();
return $this->groups;
}
}
}
alternatively, the more Java way:
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function getGroups() {
if (is_null($this->groups)) {
$this->loadGroups();
}
return $this->groups;
}
}
__get() and __set() are used for reading/writing data from/to inaccessible properties. Inaccessible properties in this case would be private or protected, never public. If they need to be public then what is wrong with just returning from the function?
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
return $this->groups;
}
}
$user = new User();
foreach($user->loadGroups() as $group)
echo $group;

Attempt to assign property of non-object error

I am getting this error and i can't see what i am doing wrong. I have done the same thing with other objects from other classes which are built in the exact same way and i can't see why i am getting this error now.
The code in which i create the object is this one:
$consulta2 = "SELECT * FROM TiposDireccion WHERE Cliente_CIF='$cif' and Direccion_Direccion='$direccion' and Direccion_CP=$cp ";
echo($consulta2."</br>");
if ($resultado2 = $conexion->query($consulta2)){
while($fila2 = $resultado2->fetch_object()){
$tipodireccion78=$fila2->TipoDireccion_Tipo;
//we see here that the select is returning a correct string with a correct value
echo($tipodireccion78);
//we try to instantiate and it fails =(
$unTipoDireccion=TipoDireccion::constructor1($tipodireccion78);
This is the class TipoDireccion:
<?php
class TipoDireccion{
private $tipo;
private $descripcion;
//Construct auxiliar
function __construct() {
}
//Constructor 1 : completo
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
function ponTipo($tipo) {
$this->tipo = $tipo;
}
function devuelveTipo() {
return $this->tipo;
}
function ponDescripcion($descripcion) {
$this->descripcion = $descripcion;
}
function devuelveDescripcion() {
return $this->descripcion;
}
}
?>
Thank you a lot in advance!
Don't know if this is still relevant to you, but in case anyone else comes on here for an answer. The problem is in this function:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
Because in the class definition, you define private $tipo; and then you try and assign $tipoDireccion->tipo to what was passed through the function. However, you aren't trying to access that variable through the scope of the class, you are trying to assign it from the 'public' scope as far as the class is concerned.
The fix for this has two options, the first one would be to change private $tipo; to public $tipo;. But that isn't a good solution as you have an assignment function for it.
Instead, use your functions that you made, which would make the function look like:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->ponTipo($tipo);
return $tipoDireccion;
}
That's how you need to access it from the public scope, which you are doing after you initiate a new one.
function constructor1($tipo) {}
should be
static function constructor1($tipo) {}

PDO: Fetch class option to send fields to constructor as array

I am wondering whether or not it is possible to elegantly map the results of a PDO query to an array member in a class rather than have them floating about as public properties of that object.
Say I have the (condensed) following:
class DBObject {
protected
$record = array();
function __construct(array $record) {
if(!empty($record)) {
$this->loadRecord($record);
}
}
}
Ideally, I want to call the constructor with an array of values passed from the database, rather than use __set or any other weird methods. So using PDO's existing API would be great.
My rough get_all function at the moment has got this far:
static function get_all() {
$class = get_called_class();
$results = DB::factory()->query('SELECT * FROM ' . $class . ' ORDER BY ID');
$results->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, $class);
return $results;
}
NB: I'm running PHP 5.3 and MySQL through PDO, and already know this problem is solveable using __set, but I explicitly want to avoid using it in favour of something more performant.
You don't need to pass arguments to a constructor to make a class with private members using PDO::FETCH_CLASS. You can do something like this:
<?php
class Songs
{
private $artist;
private $title;
public function __construct()
{
}
public function get_artist()
{
return $this->artist;
}
public function get_title()
{
return $this->title;
}
private function set_artist($artist)
{
$this->artist = $artist;
}
private function set_title($title)
{
$this->title = $title;
}
}
I'm actually doing that on a demo site that I built. It works just fine with PDO::FETCH_CLASS. By default, FETCH_CLASS creates objects by populating the fields BEFORE the constructor. Think of it as bypassing the constructor. And it will do this with private members.
If you'd rather pass arguments to the constructor you can do your query like this:
$obj = $statement->fetchALL(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Songs', $params);
In that case your constructor would look like this:
public function __construct($params)
{
$this->artist = $params[0]['artist'];
$this->title= $params[0]['title'];
}
Removed previous code
Right, can't you do something like this:
class DBObject {
protected $record = array();
function __construct($record = null) {
if(null === $record){
$obj_vars = get_object_vars($this);
$cls_vars = get_class_vars(get_class($this));
$this->$record = array_diff_key($obj_vars, $cls_vars);
}else{
$this->record = $record;
}
}
}
The problem with this however is that the values are still available as public members.
But what it will do is compare 'pre-defined' (class) members to the actual (object) members.
Since PDO will create new members in the object you can use array_diff_key to get the 'new' members.
Yes, this will still not pass them through your constructor.
How about using magic __set() method:
<?php
class MyClass
{
protected $record = array();
function __set($name, $value) {
$this->record[$name] = $value;
}
}
$pdo = new PDO("mysql:host=localhost;dbname=db", 'user', 'password');
$results = $pdo->query('SELECT * FROM table');
$results->setFetchMode(PDO::FETCH_CLASS, 'MyClass');
PHP will call this magic method for every non-existent property passing in its name and value.

How to cast an array of Objects

I'm trying to cast an array of Objects for a week already in a PHP class and have had some real issues making it to work and mainly with the logic itself as I am new to classes. Looked and read a lot of resources but it does not seem to make any sense to me, any pointers would be greatly appreciated and I am open to suggestions.
The issue:
Create a PHP class as part of the project named Contact, and then a class called 'ContactList' that contains an array of these contact objects.
And next, an array of ContactList objects called 'ContactTabs'.
Then, in the program, populate one ContactList object (named 'Contacts') with the current contacts, and create a new ContactList object named 'Friends', and add some names and email addresses there for friends. It is most important that this be done in a nice, object-oriented fashion so it can allow to create other type of contacts in the future.
A 'ContactList' object, should contain not only an array that is the list of contacts, but it would also contain the text label to put on the tab. So, it is more appropriate that the ContactList be more than a simple array, but rather it should be an object that contains an array as well as a text label.
The business logic is the following:
Contact
name
bgcolor
lgcolor
email
ContactTabs
Employees
Friends
// class definition
class Contact{
// define properties
public $name;
public $bgcolor;
public $lgcolor;
public $email;
// constructor
public function __construct() {
}
//destructor
public function __destruct() {
}
}
class ContactList extends Contact {
// constructor
public function __construct($contactname,$contactbgcolor,$contactlgcolor,$contactemail) {
$this ->name = $contactname;
$this ->bgcolor = $contactbgcolor;
$this ->lgcolor = $contactlgcolor;
$this ->email = $contactemail;
parent::__construct();
}
}
$johndie = new ContactList('John Die','#FCEDC9','#FEF9ED','somecontact1#gmail.com','9');
$johndoe = new ContactList('John Doe ','#DEEDFE','#EDF5FE','somecontact2#hotmail.com,'6');
$Friends = new ExtendedArrayObject($jp);
$Employees = new ExtendedArrayObject($elvete);
$ContactTabs= new ExtendedArrayObject($Employees,$Friends);
print_r($ContactTabs);
You had the Contact class correct (although you may want to use private/protected properties for encapsulation, but you can change that later).
This is how I would do it:
class Contact{
public $name;
public $bgcolor;
public $lgcolor;
public $email;
public function __construct($name, $bgcolor, $lgcolor, $email) {
$this->name = $name;
$this->bgcolor = $bgcolor;
$this->lgcolor = $lgcolor;
$this->email = $email;
}
}
class ContactList implements Iterator, ArrayAccess {
protected $_label;
protected $_contacts = array();
public function __construct($label) {
$this->_label = $label;
}
public function getLabel() {
return $this->label;
}
public function addContact(Contact $contact) {
$this->_contacts[] = $contact;
}
public function current() {
return current($this->_contacts);
}
public function key() {
return key($this->_contacts);
}
public function next() {
return next($this->_contacts);
}
public function rewind() {
return reset($this->_contacts);
}
public function valid() {
return current($this->_contacts);
}
public function offsetGet($offset) {
return $this->_contacts[$offset];
}
public function offsetSet($offset, $data) {
if (!$data instanceof Contact)
throw new InvalidArgumentException('Only Contact objects allowed in a ContactList');
if ($offset == '') {
$this->_contacts[] = $data;
} else {
$this->_contacts[$offset] = $data;
}
}
public function offsetUnset($offset) {
unset($this->_contacts[$offset]);
}
public function offsetExists($offset) {
return isset($this->_contacts[$offset]);
}
}
And ContactTabs would be very similar to ContactList, but would accept ContactList objects instead of Contact.
How it would work is:
// create some contacts
$bob = new Contact('Bob', 'black', 'white', 'bob#bob.com');
$john = new Contact('John', 'black', 'white', 'john#john.com');
// create a contact list and add contacts to it
$contactlist = new ContactList('Contacts');
$contactlist->addContact($bob); // using a method
$contactlist[] = $john; // using array notation
// access the list by using foreach on it, since ContactList implements Iterator
foreach ($contactlist as $contact) {
echo $contact->email;
}
First step will be to throw out all that code and start fresh. I don't know what "ExtendArrayObject" is, but you don't need it for this - it only complicates things. The rest of the code is not really on the right track.
In OOP, a class is supposed to model "something." Some kind of entity, which is often, but not always, a real-world thing. The first step in OO development is to sketch out the entities involved and model them in classes. In this case, your assignment tells you exactly what the entities are:
Contact
ContactList
So, OK, what do we need to know about our contact - what properties do we need it to have? Let's say "name" and "email." Let's give it these properties then:
class Contact {
public $name;
public $email;
}
A ContactList sounds pretty simple, it's just a list of Contacts, with a title that can be displayed in a tab. I'll write it so that it stores its Contacts internally in an array:
class ContactList {
public $title;
public $contacts = array();
}
You may be wondering, why do I need a ContactList if all it does is hold a title and store Contacts in an array? The answer is, because your assignment says you need it. :) Like many aspects of OOP, their usefulness will only be revealed as your projects increase in complexity. Just go along with it for now.
Now, put these classes in 2 separate files: Contact.php and ContactList.php. (This is not strictly necessary but is generally considered best practice.) Create a 3rd file called whatever, and in it, add the following code to tie it all together:
include("Contact.php");
include("ContactList.php");
// create a Contact object
$contact = new Contact();
$contact->name = "Bill";
$contact->email = "bill#gmail.com";
// create a ContactList object
$contact_list = new ContactList();
// set its title
$contact_list->title = "My Great Contacts";
// add our contact to it
$contact_list->contacts[] = $contact;
print_r($contact_list);
With this code, you are 80% of the way there - I didn't want to do exactly what your assignment specified because that would leave nothing left for you! I encourage you to play around with this until you feel like you really "get it." It often takes a while for OOP to "click" in one's head. But it's really crucial.
Exercises:
Add some different properties to the Contact class, like "phone" and "address"
Create some more contacts and add them to the list
Create a second ContactList object, containing a different list, with different contacts in it
Extra credit: Write a method in ContactList that adds a Contact, but only if another one with the same email address doesn't already exist in the list.

I need to collect an array of classes to call their static variables in php

First thing i want to say that it's not an easy question to explain, so please be patient if it seems confusing.
I have a set of classes like this
class Product {
public static $static_type = 'product';
public static $static_table = 'product_table';
public function __construct($params) { //do some }
}
and then there are the classes News, Events etc
From another class i need to access to those static variables inside these classes in an iterative way. Something like:
//...
if (Product::$static_type) { //do some }
else if (News::$static_type) { //do other }
//...
I want to trasform it in a cycle, like foreach in a way like this (it's not correct but makes sense to my question)
foreach ($classes as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
So i think about a singleton/static class that has a static method returning an array of my classes (not instantiated). Like this:
class Conf {
public function __construct() {
//nothing
}
public static function get_class_array () {
//how to do this???
}
}
and then
foreach (Conf::get_class_array() as $class) {
echo $class::$static_type; //brrrr, just to render the idea :)
}
How i can reach this? I don't want to instantiate Product, News or others in this case.
Edit: eval is evil, i don't want to use it. No tricks with get_declared_class, if there's no way to solve I will use reflection, that i think it's the more elegant way among the mentioned :(.
Edit: in the meantime i'll do the Conf::get_class_array() in this way
public static function get_class_array () {
return array(new ReflectionClass('Prodotto'), new ReflectionClass('News'));
}
and then call it here:
foreach (Conf::get_class_array() as $class) {
echo $class->getStaticPropertyValue('static_type');
}
I don't think you can do this. You could however do one of these:
$properties = get_class_vars('Product');
echo $properties['static_type'];
or
$class = new ReflectionClass('product');
echo $class->getStaticPropertyValue('static_type');
Note that in PHP 5.3 echo $class::$static_type; will work (http://php.net/manual/en/language.oop5.static.php)
Until 5.3.0, you can try this method. Create a container class as you suggested (we'll call it Conf to stick with what you had), and provide two methods for setting and getting applicable classes that you want to iterate over:
<?php
class Conf {
private static $instance;
private $classes = array();
public static function getInstance() {
if ( is_null(self::$instance) ) {
self::$instance = new self();
}
return self::$instance;
}
public function registerClass($className) {
// Use associative index to maintain uniqueness
$this->classes[$className] = $className;
}
public function getRegisteredClasses() {
return $this->classes;
}
}
Some example classes and how to register them:
class X {
public static $a = "catus";
public static $b = "pants";
}
class Y {
public static $a = "apples";
public static $b = "bananers";
}
$conf = Conf::getInstance();
$conf->registerClass("X");
$conf->registerClass("Y");
Now, to access and/or alter the static members, you can do something like the following (using RefelectionClass as tom Haigh pointed out):
$conf = Conf::getInstance();
echo "<pre>";
foreach ( $conf->getRegisteredClasses() as $class ) {
$reflection = new ReflectionClass($class);
echo "<hr/>Class: $class\n";
// Access example
print_r( $reflection->getStaticProperties() );
// Alter example
$reflection->setStaticPropertyValue("a",
$reflection->getStaticPropertyValue("a") . "-modified"
);
print_r( $reflection->getStaticProperties() );
}
If you have a class naming convention like Com_Example_Static_X and Com_Example_Static_Y, you can simplify Conf->getRegisteredClasses() (and even make it a static method if you so desire) by doing as n3rd suggested:
class Conf {
// ....
static public function getMatchingClasses($pattern="/^Com_Example_Static_.+$/") {
$response = array();
foreach ( get_declared_classes() as $className ) {
if ( preg_match($pattern, $className, $m) ) {
$response[] = $className;
}
}
return $response;
}
}
And, of course, update your foreach to:
foreach ( Conf::getMatchingClasses() as $class ) {
// ...
}
Hope that was helpful.
You can use get_declared_classes() to get a list of classes. This will be all class though, not just the ones you've declared.
You should make all your classes inherit from a base class:
class Product extends MyBase {}
Then you can list the classes like this
function get_class_array()
{
$myClasses = array();
foreach (get_declared_classes as $class)
{
if (is_subclass_of($class, 'MyBase'))
$myClasses[] = $class;
}
return $myClasses;
}
Then you can get the data like this:
foreach (get_class_array() as $class)
echo eval("return $class::\$foo;"); // Yes yes, eval is evil, we know...
To get a list of classes, you can use get_declared_classes. Then you'll have to determine which of those classes you want to process.
You could do this by looking for a common base class with is_subclass_of, or using ReflectionClass to see if it has the static member variables you are interested in.
I don't think there's an easy way to do this. Here are a few ideas off the top of my head how you could go about doing this:
Use get_declared_classes() to retrieve a list of all defined classes and check them against your naming scheme (e.g. MyNamespace_*) or whether they implement an interface (e.g. MyStaticEnumerable).
Kinda like the above, but a little more sophisticated: write your on class loader and have it check whether a loaded class is one of ones you want to enumerate. If so, make it known to some manager class.
Check the directory in which the classes are defined to manually enumerate all classes.

Categories