How to delete an object from within itself? - php

I have an array of objects (A) which has an array of objects (B) inside it.
I'm trying to move B to a different object in array A.
I'm trying to use this:
public function killToken($a) {
array_push($a->tokens,$this); // Put this token in new array (works)
unset($this); // Remove token from this array (does not work)
}
I call this function via: $b->killToken($a);
I've tried several variations on this, but I just can't figure out how to get rid of the object from inside itself.
Any help would be appreciated.

In my opinion, you're breaking encapsulation by trying to do this:
array_push($a->tokens,$this);
You should not be modifying $a's state from within $b. You should only modify $b's state from within $b, and tell $a to modify its own state:
$b->killToken($a); // only removes $a from $b->tokens
$a->addToken($b); // adds $b to $a->tokens
This is one of the basic principles of OO design.
Edit: That being said, unset($foo) is not how you remove an element from an array. You can array_search() for the element, which will give you the index, and then you can unset the index like unset($array[$index]), and there are a few other different methods, as well.

Related

Setting a property on a target object only if it exists in a source object, using a helper function

I'm writing a piece of data processing code that often has to set a property on a target object to the value of a property of a source object, but only if that property exists in the source object, like so:
if (property_exists($source, 'something')) {
$target->other = $source->something;
}
(I don't use isset() because null values should also be passed on.)
Repeating the above all over the code would be a pain. Right now I'm using this function to do it:
function set_if_exists($target, string $targetProperty, $source, string $sourceProperty)
{
if (property_exists($source, $sourceProperty)) {
$target->$targetProperty = $source->$sourceProperty;
}
}
which for the example above would be
set_if_exists($target, 'other', $source, 'something');
Is there a way to achieve the same without having to name the properties as strings? Ideally I would like to be able to do something like
set_if_exists($target->other, $source->something);
but I have no idea how to achieve this. Is it even possible?
array_key_exists only works on arrays, you are using objects. You should use the property_exists function to know if an object has a specific property.
You cannot do this the way you want to because a non-existing property will always return null, so there is no way to know if the property exists or not from the value.
I have concluded that this is not possible. There does not seem to be any mechanism in PHP that would allow you check for the existence of a (potentially deep) property, the way that the isset() builtin or ?? can.

Maintain php object state

My php code creates a "School" object which is (among other functions) able to return several forms which are being ,on submit, handled by php.
Thru one of these forms i'm able to add a "SchoolClass" object to a array in the "School" object but it seems that the "School" object is recreated at some point after i add the "SchoolClass" object. This re-creation makes the array of "SchoolClass" disappear.
The functions that returns the forms are static, but the functions where "SchoolClass" objects are added to the array are not static; ("School::ShowStudentForm() and School->RegisterSchoolClass($class)")
Both the class definition and the php-script which handles the form after submit is in the same file(index.php). I had the idea that keeping both the class code and the form handling code in the same file would do the trick, but it did not make a change.
Question 1: how do i maintain the "School" object for as long as i need it?
Question 2: is it ok to create a class with only static methods and write the data directly to a file or database to avoid the whole problem or just skip the whole object approach and just use "normal" functions?
Yes, this problem is probably encounterd before and therefore also answered before but i have not been able to find a answer which i understand.
OK....i figured it out.
I can use the $_SESSION variable to store it.
Now i do like this in the start of the page:
$school = null;
if(!isset($_SESSION["school"]))
{
$school = new School;
$_SESSION["school"] = $school;
echo "New school <br>";
} else {
$school = $_SESSION["school"];
}

How to use usort on an object with an array of arrays

Simply put: I need to sort an array of objects based on a specific value of a specific array element inside a class property.
This would be great, if it worked:
In this picture, the code illustrates what I want: I want to be able to tell usort to look in a specific array element and find the priority key, and sort based on that integer value.
So, for a given hook (hook-foo), I would need the comparison to be done on:
$plugin->hooks['hook-foo']['priority']
For another hook (hook-bar), for which the same plugin might act upon, I need the sort to look at:
$plugin->hooks['hook-bar']['priority']
if usort supported me sending an arbitrary argument to it, this would be solved. But, the code pictured below was written to illustrate my point and then tested because "maybe it will work". It didn't.
How do I rearrange this to get the sort I need?
(Note: Plugin::hooks is an array of arrays and represents all the metadata required to execute a plugin on a given hook. So, it may contain 'hook-foo' and 'hook-bar', which do not execute at the same time. It may also contain one or the other, or... neither.)
Edit: Here are the example structures, which may help illustrate my point. This shows 2 of 14 plugins loaded.
You can see that each plugin responds to multiple different hooks.
So, if I wanted to control the order for a given hook, I should only have to change the priority of that hook.
For example, here: you can see the cli-init hook fires a method of the class declareMySelf, which simply says: "Plugin XYZ loaded" in the command line interface. Both of the priorities are 50, so it will display them in whatever order the system finds the plugins.
But, if I wanted to display "PHP Mailer loaded" BEFORE I display "Config / Settings management loaded", all I should have to do is drop the PHP Mailer plugin priority to 40.
You should probably have a $hookSearchKey property within your class and set it with a setHookSearchKey() method when needed:
class Whatever {
// ...
private $hookSearchKey
public function setHookSearchKey($key) {
$this->hookSearchKey = $key;
}
//... this is your method of sorting:
public function fooSort(...) {
$this->setHookSearchKey('hook-foo');
usort(...); // do your thing
}
//... this is your method of sorting:
public function barSort(...) {
$this->setHookSearchKey('hook-bar');
usort(...); // do your thing
}
//...
public function sortByHookPriority(...) {
if($a[$this->hookSearchKey] == $b[$this->hookSearchKey]) return 0;
// ...
}
}
Would this work for you?

PhP variable declared in a loop and I would like to store it in a global array

Im new to PHP and would like to get some advice on the following situation:
I have a file in which Im parse an XML file. My strategy has been create a "parser.php" file. Then create separate files for each tag in the XML file. Each of these separate files is a class that when instantiated will store all the attributes of that particular tag.
I have several loops in the parser that instantiate the class that corresponds with the tag when they encounter it.Once the loop is done that object is then inserted into a global array.
Here is the code:
<?php
include ('class.Food.php');
include ('class.Drink.php');
$xml = simplexml_load_file("serializedData.xml");
//Global Variables==============================================================
$FoodArray = array();
$DrinkArray = array();
$FoodObj;
$DrinkObj;
foreach($xml->children() as $child)
{
//Instantiate a Food object here and insert into the array
$FoodObj = new Food();
//Add attributes of tag to the Food object
array_push($FoodArray, $FoodObj);
}
Thus at the end of each loop there would be a food object created and that would be added to the FoodArray.
My questions are:
Do I have to explicitly call a destructor or free the object memory at the end of the loop?
Using the above syntax, will the FoodObj be stored in the FoodArray as a reference or a value
Each time the variable Food gets a different instance stored in it, does that matter when stored in the array? All the objects stored in the array go by the index number right?
Thanks
If you 'free' the Food() object at the end of the loop, you'd be killing the last reference to it you pushed into $FoodArray as well. PHP will clean up for you and delete things when they go out of scope. Since you're assigning to a global array, the scope is the whole script. So unless you explicitly take steps to delete an item from $FoodArray, the objects will be kept around until the script exits.
http://www.php.net/manual/en/language.oop5.references.php
array_push puts elements onto the end of the array, so they'll get an incrementing index number.
Try the following
$FoodArray = array();
$DrinkArray = array();
foreach($xml->children() as $child)
{
//Instantiate a Food object here and insert into the array
$FoodObj = new Food();
//Add attributes of tag to the Food object
$FoodArray[] = $FoodObj;
unset($FoodObj);
}
1) unset'ng the instance manually is good form, but unnecessary. the php garbage collector will do it for you anyways and is very efficient.
2) in the example above, $FoodArray[i] will continue to point to the instance even after $FoodObj is unset.
3) it works whether $FoodObj is local to the loop or not. But for best practice it should be.

method chain hijacking

This isn't a real fluent interface. I have an object which builds up a method stack. Which gets executed by a single function call. But now I might add another virtual method, which "takes over" that method stack.
Use case: I'm wrapping my superglobals into objects. This allows me to "enforce" input filtering. $_GET and co provide simple sanitizing methods. And my new version now allows chaining of atomic filters. As example:
$_GET->ascii->nocontrol->text["field"]
This is a method call. It uses angle brackets. But that's just a nice trick which eases rewriting any occourence of $_GET["field"]. Anyway.
Now there are also occasionally forms with enumerated fields, as in field[0],field[1],field[2]. That's why I've added a virtual ->array filter method. It hijacks the collected method stack, and iterates the remaining filters on e.g. a $_POST array value. For example $_POST->array->int["list"].
Somewhat shortened implementation:
function exec_chain ($data) {
...
while ($filtername = array_pop($this->__filter)) {
...
$data = $this->{"_$filtername"} ($data);
...
}
function _array($data) {
list($multiplex, $this->__filter) = array($this->__filter, array());
$data = (array) $data;
foreach (array_keys($data) as $i) {
$this->__filter = $multiplex;
$data[$i] = $this->exec_chain($data[$i]);
}
return $data;
}
The method stack gets assembled in the $this->__filter list. Above exec_chain() just loops over it, each time removing the first method name. The virtual _array handler is usually the first method. And it simply steals that method stack, and reexecutes the remainder on each array element. Not exactly like in above example code, but it just repeatedly repopulates the original method stack.
It works. But it feels kind of unclean. And I'm thinking of adding another virtual method ->xor. (YAGNI?) Which would not just iterate over fields, but rather evaluate if alternate filters were successful. For example $_REQUEST->array->xor->email->url["fields"]. And I'm wondering if there is a better pattern for hijacking a function list. My current hook list ($this->__filter) swapping doesn't lend itself to chaining. Hmm well actually, the ->xor example wouldn't need to iterate / behave exactly like ->array.
So specifically, I'm interested in finding an alternative to my $this->__filter list usage with array_pop() and the sneaky swapping it out. This is bad. Is there a better implementation scheme to executing a method list half part me -> half part you?
I've made a similar chaining interface before, I like your idea of using it on GET/POST vars.
I think you will be better off doing something like $var->array->email_XOR_url; rather than $var->array->email->XOR->url;. That way you can catch the various combinations with your __get/__call magic.

Categories