how to concat a counter for an object declaration in PHP - php

I am trying to create multiple instances of an object as this was how the wrapper was designed to work. Now my problem is that I wanted to add a counter in the object declaration so that I will just have to loop through my data and create the necessary object for it and also loop it again for the wrapper to read them all.
Currently, I have this:
if(sizeof($product_name) > 0){
for($counter=0;$counter<sizeof($product_name);$counter++){
$lineitem.$counter = new LineItem($this->_xi);
$lineitem.$counter->setAccountCode('200')
->setQuantity($product_qty[$counter])
->setDescription($product_name[$counter])
->setUnitAmount($product_price[$counter]);
print_r($lineitem.$counter);
}
}
print_r($lineitem0);
My print_r returns nothing for both inside and outside the loop.

Your problem is not really about OOP, but more about php. You want to create dynamic variable name to store all the instances of your class you are creating, you should do:
if(sizeof($product_name) > 0){
for($counter=0;$counter<sizeof($product_name);$counter++){
${"$lineitem$counter"} = new LineItem($this->_xi);
${"$lineitem$counter"}->setAccountCode('200')
->setQuantity($product_qty[$counter])
->setDescription($product_name[$counter])
->setUnitAmount($product_price[$counter]);
print_r(${"$lineitem$counter"});
}
}
print_r(${"$lineitem" . 0});
Have a look at this question: Dynamic variable names in PHP

Related

Looping with Object Operators

I am playing around with PHP Calendar (Corey Worrell) and had a question regarding instance looping. In order to initialize the calendar, I have to output this:
$calendar->standard('today')
->standard('prev-next')
->standard('holidays')
->attach($event1)
->attach($event2)
->attach($event3)
->attach($event4)
->attach($event5)
->attach($event6)
->attach($event7)
->attach($event8)
->attach($event9)
->attach($event10)
->attach($event11)
->attach($event12)
->attach($event13)
->attach($event14)
->attach($event15)
->attach($event16)
->attach($event17);
Each ->attach($event#) outputs an event on the calendar. I want to loop through these numerically incrementing event names but adding a for loop anywhere in that code breaks everything, outputting this error:
PHP Catchable fatal error: Argument 1 passed to
Event_Subject::attach() must be an instance of Event_Observer, null
given, called in /calendar/index.php on line 75 and defined in
/calendar/classes/event_subject.php on line 21
Here is the loop I've tried:
$calendar->standard('today')
->standard('prev-next')
->standard('holidays')
for ($inc = 0; $inc <= $number_of_events; $inc++) {
if ($inc == $number_of_events) {
->attach($$event_name);
}
else {
->attach($$event_name)
}
}
How can I loop inside of here? My events are stored in MySQL and I am doing an $number_of_events = $result->num_rows to determine the number of events returned. The ->attach($event#) would loop, repeating until the total $number_of_events is hit.
That's called method chaining. Each method of the class returns an instance of the called object via $this, allowing you to stack method calls. Method chaining is possible because the function returns a reference to your object.
Because each function in the class that supports method chaining returns the calling object, you can just re-assign the returned object back into the original $calander variable;
for ($inc = 0; $inc <= $number_of_events; $inc++) {
if ($inc == $number_of_events) {
$calander = $calander->attach($event1);
}
else {
$calander = $calander->attach($event1);
}
}
Additionally, if you wanted to iterate through variable names, you could use a variable variable inside your loop;
$variable = "event".$inc;
$calander = $calander->attach($$variable);
So this would become $event0, $event1, $event2, etc.
The problem with the loop is that there is nothing in front of the -> operator. The -> references a property of an object, but you do not provide it with an object. You could solve it by putting $calendar in front of the lonesome operators ($calendar->...), but it would still not be very pretty code.
I suggest this instead:
I think you could just add the events one by one inside the loop I assume you are already using to create the $event1, $event2, etc. I don't know what you are using to get the data out of the database or what your table structure looks like, but I will provide an example for MySQLi. It should be easy to modify for other alternatives.
//Add the standards.
$calendar->standard('today')
->standard('prev-next')
->standard('holidays');
//Connect to the database here and query for the events using MySQLi.
//Loop through the results.
while($row = $result->fetch_assoc()) {
//Create an event from the database row...
$event = calendar->event()
->condition('timestamp', $row['TIMESTAMP'])
->title('Hello All', $row['TITLE'])
->output('My Custom Event', $row['OUTPUT']);
//...and attach it.
$calendar->attach($event);
}
This is not code to be copy-pasted directly, but more of a suggestion about how to organize it.
Also, for the future, you should not name variables $name1, $name2, etc and then use $$ to reference them. Use arrays instead.
Here's the solution. Not sure why changing this to reference $calendar each line worked, but it did. Thanks everyone.
$calendar->standard('today');
$calendar->standard('prev-next');
$calendar->standard('holidays');
for ($x = 1; $x <= $number_of_events; $x++) {
$event_name = "event".$x;
$calendar->attach($$event_name);
}

How to retrieve value from returned object in php?

How to retrieve value from a complicated object structure in php? I know using '->' operator we can access the value but I am very confused in the object I am returned with. From the object returned, I want to fetch the character value. How do i do that?
I am using Neo4jPHP and trying to execute a cypher query "MATCH (n) RETURN distinct keys(n)" to return all distinct property keys. After doing a var_dump of the row object, the partial output is shown below.
Edit:- My edited code after following Mikkel's advice:-
$keyquery="MATCH (n) RETURN distinct keys(n)";
$querykey=new Everyman\Neo4j\Cypher\Query($client, $keyquery);
$resultkey = $querykey->getResultSet();
foreach ($resultkey as $row)
{
for($i=0;$i<count($row[0]);$i++)
{
echo $row[0][$i]; // returns all the property keys from the Row object
}
}
You can't access the object property directly as it was declared as protected (only accessible from within the class or an inheriting class).
However, in such a case, the developer has usually added an object method or overloading function that allows you to access the information you're looking for. Taking a peek at the source, it looks like you should be able to access the data you're looking for using either:
// this works because the class implements Iterator
foreach ($myobject as $row) {
echo $row['keys(n)']; // outputs "character"
}
or:
// this works because the class implements ArrayAccess
// don't ask me why they put keys and values in different arrays ('columns' and 'raw')
echo $myobject[0]['keys(n)']; // outputs "character"
If you look the class Row you will find out that you can access it treating the object like an array.
$character = $myRow[0];
Looking at the object you dumped here, you can see that the object is implementing \Iterator, \Countable, \ArrayAccess, which means you can basically treat it like an array. The underlying data source is the protected $raw.
$queryResult = ...;
foreach ($queryResult as $row) {
echo $row['character'] . PHP_EOL;
}
The value you are looking for is protected and not accessible,
try find object class and add function to retrieve the value.
use regular expression to extract the portion, which is not recommended:
/\'character\'(length\=(.*?))/

asXML() not saving changes

I have a complex xml with nested namespaces for which I'm trying to do the following:
1) Open XML File
2) Validate against a XSD Schema
3) Parse it
4) Change nodes (1 at the time, setting them either to null or other variables)
5) Saves changed xml into a new file
5) Ri-validate it against same schema as 2) and make sure an error pops up.
Now, points 1-2-3 and 5-6 are not an issue. The Change + saving into a new xml is.
XML Snippet:
<Movie creationDateTime="2014-05-14T13:42:52Z" endDateTime="2015-05-14T00:00:00Z" providerVersionNum="5" startDateTime="2014-05-14T00:00:00Z" uriId="disney.chlsd.com/MOOT0000000000020902">
<core:Ext>
<ext:MovieExtensions analogueOff="true" mediaId="CGOT0000000000020902">
<ext:assetPart partNum="1">
<ext:SourceUrl>DSNY0000000000020902.mxf</ext:SourceUrl>
<ext:ContentFileSize>46166173874</ext:ContentFileSize>
<ext:ContentCheckSum>4da3e4cafd4f3262d136c519311a7b53</ext:ContentCheckSum>
<ext:SOE>PT09H59M30S00F</ext:SOE>
<ext:SOM>PT10H00M00S00F</ext:SOM>
<ext:EOM>PT10H46M02S11F</ext:EOM>
</ext:assetPart>
<ext:playlistSupportOnly>false</ext:playlistSupportOnly>
</ext:MovieExtensions>
</core:Ext>
<content:AudioType>Stereo</content:AudioType>
<content:FrameRate>25</content:FrameRate>
<content:Codec>H.264</content:Codec>
<content:AVContainer>MXF</content:AVContainer>
<content:Duration>PT00H46M02S</content:Duration>
<content:IsHDContent>false</content:IsHDContent>
</Movie>
I do the parsing on attributes using ($mypix is the XmlSimpleObject where I load the Xml):
$xmlfile = "prova.xml";
$mypix = simplexml_load_file($xmlfile);
[...]
foreach ($mypix->children() as $parent => $child)
{
echo "<br/>Main Node: ".(String)$parent."<br/>";
foreach ($mypix->children()->attributes() as $a => $b)
{
echo "Main attribute: ".(String)$a. " with value: ".(String)$b."<br/>";
if ($a == "endDateTime")
{
echo "Entering node: ".$a." and eliminating: ".$b." <br/>";
$b=NULL;
echo "<br/><pre>";
echo $mypix->asXML("t.xml");
echo "<br/></pre>";
}
}
}
The parsing gives me:
Main Node: Movie
Main attribute: creationDateTime with value: 2014-05-16T14:40:41Z
Main attribute: endDateTime with value: 2015-05-16T00:00:00Z
Entering node: endDateTime and eliminating: 2015-05-16T00:00:00Z
Problem is, when I open t.xml, endDateTime is still a valid tag (definitely not empty).
=========================================================================
Things I've tried:
alternative approach using Xpath:
$namespaces = $mypix->getNameSpaces(true);
$mypix->registerXPathNamespace('ext', 'URN:NNDS:CMS:ADI3:01');
$mypix->registerXPathNamespace('title', 'http://www.cablelabs.com/namespaces/metadata/xsd/title/1');
$mypix->registerXPathNamespace('core', 'http://www.cablelabs.com/namespaces/metadata/xsd/core/1');
echo "<br/><br/>";
// Getting Episode Name
$xtring = ($mypix->xpath('//core:Ext/ext:LocalizableTitleExt/ext:EpisodeName'));
echo "<br/><b>EpisodeName: </b>".$xtring[0]."<br/>";
$xtring[0] = NULL;
echo $mypix->asXML("t.xml"); // Nothing again
Here the xpath query returns a valid value, but changing & writing to a new file fails
2nd try: save to the same file ('prova.xml') instead of 't.xml' (in case I screwed up with SimpleXMlObjects)...nothing...
Any help please?
Setting a variable to null does not remove, destroy, or edit the object that variable used to point to.
You may have seen examples where this is a valid way of "cleaning up" something like a database connection object, because when you remove all references to an object, its destructor will be called. However, this is not the case here, because the object pointed at by $b is still accessible, e.g. from another call to $mypix->children()->attributes().
The other thing you will have seen in examples is assigning a new value to a child element or attribute using syntax like $element->someChild = 'new value'; or $element['someAttribute'] = 'new value';. However, this works because SimpleXML overloads the property access (->) and array element access ([...]), in the same way as implementing __set() and ArrayAccess::offsetSet(), and your code uses neither of those.
There is a way of using the array-access overload to delete or blank an element which you have a variable pointing at directly, which is that the offset [0] points back at the current element. Thus, you can write unset($b[0]); to delete an element or attribute completely; you can also write $b[0] = ''; to blank an element, but with an attribute as here, that leads to a fatal error (which I suspect is a bug).
Note that when you use XPath, you are not actually reaching this self-reference, or an overloaded operator because SimpleXMLElement::xpath returns a plain array, so $xtring[0] is just a normal PHP variable. Since it's an element in that example, you could delete it using the self-reference, by writing unset($xtring[0][0]); or blank it with $xtring[0][0] = '';
However, all that being said, your code can actually be massively simplified in order to avoid any of this being necessary. Let's take it apart line by line:
foreach ($mypix->children() as $parent => $child)
The variable $mypix here is for a larger document than you show in your sample, the sample apparently being just one entry in this loop. Note that $parent => $child here would be more appropriately named $childName => $child.
It's also quite likely that you're only interested in children with a particular name, so the most common form of loop is foreach ($mypix->Movie as $child)
foreach ($mypix->children()->attributes() as $a => $b)
Here you ignore the progress around the outer loop completely, and go back to the whole document. SimpleXML will interpret $mypix->children()->... as $mypix->children()[0]->..., that is only ever look at the first child. You actually want foreach ($child->attributes() ....
if ($a == "endDateTime")
Since you are looking for an attribute with a particular name, you don't actually need to loop over attributes() at all, you can just access it directly as $child['endDateTime']. Note that since we're now using the overloaded [...] operator, we can make use of it to write back to or delete the attribute.
echo $mypix->asXML("t.xml");
SimpleXMLElement::asXML either returns the document as a string or saves to a file, not both. Since in the latter case it returns a boolean, echoing that result isn't likely to be very useful.
You are also calling this function every time around the inner loop, thus saving the same file several times. You only need to do it once, when you've finished making all your modifications.
So, here is how I would write that code:
foreach ( $mypix->Movie as $child )
{
$child['endDateTime'] = null;
// or to remove the attribute completely: unset($child['endDateTime']);
}
$mypix->asXML('t.xml');
Or, for the second example but without XPath (long-winded, but useful if you are changing several things at once, so don't want to "jump" to the deepest descendants in the document). Note the use of ->children($ns_uri) to switch to a different namespace.
// Constants for handier but implementation-independent reference to namespaces
define('XMLNS_EXT', 'URN:NNDS:CMS:ADI3:01');
define('XMLNS_TITLE', 'http://www.cablelabs.com/namespaces/metadata/xsd/title/1');
define('XMLNS_CORE', 'http://www.cablelabs.com/namespaces/metadata/xsd/core/1');
foreach ( $mypix->children() as $child )
{
foreach ( $child->children(XMLNS_CORE)->Ext as $ext )
{
foreach ( $ext->children(XMLNS_EXT)->LocalizableTitleExt as $title )
{
// Delete a child node; note not ->children() as "ext" namespace already selected
unset($title->EpisodeName);
}
}
}
$mypix->asXML("t.xml");

using arrays from one file in nested functions in another file

I have a PHP file which contains associative arrays. I have them all in one file for ease of editing/updating. For example:
$ass_first = array( "title"=>"Title",
"first_name"=>"First Name",
"surname"=>"Surname",
"phone"=>"Phone Number",
"email"=>"Email Address",
"add1"=>"House Name/Number",
"add2"=>"Street",
"add3"=>"Town",
"add4"=>"City",
"add5"=>"Post Code"
);
In another file, I have functions which are passed variables, which in turn call other functions.
For example I have a function which is called to create a table containing form inputs. Inside this function, there is a test to see what form input is needed and the necessary function is called.
function create_table($titles, $id) { //$titles is the relevant array from lists.php, $id is the id of the containing div
$select = array('timescale','bus_route','train_stat'); //'select' id list
foreach ($titles as $k=>$v) { //$k is the database/id/name $v is the description text
if (in_array($k,$select)){
select_box($k,$v);
}
else if //input is a text area
text_box($k,$v);
}
else{ //input is a checkbox
check_box($k,$v);
}
}
}
Inside these three 'nestled functions' I want to refer to some arrays contained in the lists.php file.
I want to know the best way to add these arrays to the functions scope. I have been told variable variables arent neccessarily the best way to go, neither is declaring global variables. I cant think of any other way to do it!
Also passing them as a variable into the functions isnt really an option because the three different nestled functions need a different set of arrays meaning three sets of arrays will be passed but only one would be used at any one time!
You can pass the arrays to your create_table function:
function create_table($titles, $id, $arrays) {
...
}
You can go ahead and use a global variable, maybe add a comment above it saying what file sets the variable:
function create_table($titles, $id) {
//$arrays gets set in lists.php
global $arrays;
...
}
Or you can require your lists.php file like so:
function create_table($titles, $id) {
require 'lists.php';
...
}

seems like foreach loop doesnt work

I want to check an object props, but it looks like loop never runs.
$object = $this->helix->Footer();
// var_dump($object) ; // the var dump starts with "object(Helix)#118 (9) { ....."
foreach($object as $prop_name => $prop_val){
echo $object->$prop_name ;
}
Does anyobody have idea what Im doing wrong?
You can use this function also: php.net/get_object_vars
But the issue can be the same. This function can show only the properties it can see.
It means, if you call it outside the class, then only the public vars. But is you call it inside the class, then "everything". (see the comments on the manual page.)

Categories