php: call a method of an object coming from an array - php

I'm trying to figure out a way to call a method of a specified member (Class A) coming from an array of members in class B.
<?php
class A
{
public function do_something()
{
echo "class A did something";
}
}
class B
{
private $arr = array();
private $current_index = 0;
public function add_new_A()
{
$new_a = new A;
array_push($this->arr, (object) [
$this->current_index => $new_a
]);
$this->current_index++;
}
public function get_an_A_by_index($index)
{
return $this->arr{$index};
}
public function do_something_with_A_member_inside_array($index)
{
self::get_an_A_by_index($index)->do_someting();
}
}
$b = new B;
$b->add_new_a();
$b->add_new_a();
echo print_r($b->get_an_A_by_index(0));
echo "\n";
$b->do_something_with_A_member_inside_array(0); // returns error
// console:
// stdClass Object ( [0] => A Object ( ))
// Uncaught Error: Call to undefined method stdClass::do_something();
?>
To wrap things up, I want to know if my approach is considered bad and/or if there is something I can do to fix the error. Before you disagree with my code, take a look at my php code I'm actually working on. That's all for my question.
Now about why I want to call a method of a member A inside . For my assignment I want a program that does something seperately with the method do_something of class a. So far, the only way to do that is by storing seperate members of A.
I'm not sure if I'm approaching this wrong or not, because I'm coming from Java. So, The first thing I came up with was the approach shown above. When I do this in Java, it works fine. But php is different from Java and I'm still learning how php works since I'm new to it.

Your code isn't far off, you've just got an issue with the way you're building up the collection of A objects.
array_push($this->arr, (object) [
$this->current_index => $new_a
]);
This is creating a data structure that I'm pretty sure isn't what you expect. You'll end up with an array full of stdClass objects, each with a single A member and its own internal index:
Array
(
[0] => stdClass Object
(
[0] => A Object
(
)
)
)
You're then retrieving the stdClass object and trying to run the method on that, hence the Call to undefined method stdClass::do_something... error you're seeing.
Instead, all you need to do is this:
$this->arr[$this->current_index] = $new_a;
The rest of your code is just expecting an array of A objects, nothing nested any deeper.
I've put a full example here: https://3v4l.org/ijvQa. Your existing code had a couple of other typos, which are also fixed. You'll spot them easily enough if you turn on error reporting.

Related

Query returns content from previous functions

I have a Chat.php file containing everything query-related. Then there is a Core.php containing connection to database and basic functions used in Chat.php like "query" and "rows" which processes the "query" into array.
In Chat.php there are two functions, the second one printing content of the first when using print_r($this->rows());. checkForLastMessage() is supposed to check one table to see if there are new messages to be pulled from another table with function getNewMessages().
This is how it looks like:
Core.php
<?php
class Core
{
...
public function query($sql)
{
$this->result = $this->db->query($sql);
}
public function rows()
{
for($x = 1; $x <= $this->db->affected_rows; $x++)
{
$this->rows[] = $this->result->fetch_assoc();
}
return $this->rows;
}
}
Chat.php
<?php
class Chat extends Core
{
public function checkForLatestMessage($chatid, $iam)
{
$userinchat='for'.$iam;
$this->query("SELECT anonchat.$userinchat FROM anonchat WHERE anonchat.chatid=$chatid");
$printarray = Array();
$printaray = '';
foreach( $this->rows() as $id )
{
$printarray[] = $id[$userinchat];
}
if($printarray[0] != '')
{
$this->getNewMessages($chatid, $printarray[0]);
}
}
public function getNewMessages($chatid, $requiredMessages)
{
$this->query("SELECT anonmessage.content, anonmessage.timeposted FROM anonmessage WHERE anonmessage.messageid IN ($requiredMessages) ORDER BY anonmessage.timeposted ASC");
print_r($this->rows());
}
The last print_r contains elements from the previous function. I don't know why that is.
Edit. This is the output:
Array ( [0] => Array ( [for1] => 2,4,6 ) [1] => Array ( [content] =>
Message 2 [timeposted] => 2017-08-04 16:12:34 ) [2] => Array (
[content] => Message 4 [timeposted] => 2017-08-04 16:12:48 ) [3] =>
Array ( [content] => Message 6 [timeposted] => 2017-08-04 16:13:03 ) )
Element [0] of array (the one with "for1") is remaining from previous function.
To answer your question, you're initializing the class property $this->rows in the first method and then the 2nd method is appending to it. You need to reset $this->rows before adding to it.
public function getNewMessages($chatid, $requiredMessages)
{
$this->rows = null;
$this->query("SELECT anonmessage...");
print_r($this->rows());
}
Or better yet, reset the variable in the query() method. That way you don't have to do it each time.
Please don't take offense when I say that this is a bad design.
You're trying to write your own DAL (Data Abstraction Layer). This is a very complicated task and there are already lots of implementations out there. The Core class is going to become massive, complicated, and unwieldy when you try to adapt it to a dozen other classes.
PHP only supports a single inheritance so right off the bat you shot your self in the foot because any class that needs DB interactions will have to extend Core and you won't be able to extend anything else.
Consider keeping things simple for now and let each method handle their own queries and DB interactions. Focus on major concepts like DRY, encapsulation and keeping your classes focused on their responsibility.
Ex. checkForLatestMessage() what is this supposed to do? It sounds like should check for messages and then return a boolean (true|false) but instead it is calling getNewMessages() which outputs some data.
I don't know enough about your application to really suggest something useful but this feels a bit better than the path you're heading down. Notice we're not inheriting from Core so you're free to inherit from something else. The methods are concise and do what they say. You'd probably also save a few lines of code this way and it's easier to read.
<?php
class Chat
{
public function hasNewMessages($chatid, $iam)
{
// Query using PDO properly
return (bool)$hasNewMessages;
}
public function getNewMessages($chatid, $requiredMessages)
{
// Query using PDO properly
// An array of data or objects
return $messages;
}
}
/******** Client Code ***********/
$chat = new Chat();
if ($chat->hasNewMessages()) {
foreach ($chat->getNewMessages($id, $required) as $message) {
// $message
}
}
Just some of my thoughts... good luck.

Setting Properties via Methods

I am new to OOP.
I am currently working on adding data to an object to then submit it to a database. I have created a method called setData to take two arguments, and then add those arguments into the object.
Inside my class, I use this
public function setData($value1, $value2) {
$this->$value1 = $value2;
}
Where on a form submit, that function is used to store data
$sites = new AddWebsites;
$sites->setData('name', $name);
$sites->setData('page_rank', $pr);
$sites->setData('pa', $pa);
$sites->setData('da', $da);
$sites->setData('tf', $tf);
$sites->setData('cf', $cf);
$sites->setData('keywords', $keywords);
$sites->setData('notes', $notes);
This will output the following data
AddWebsites Object
(
[name] => asdf.com
[page_rank] => 5
[pa] => 15
[da] => 25
[tf] => 14
[cf] => 62
[keywords] => Array
(
[0] => kw1
[1] => kw2
[2] => kw3
[3] => kw4
[4] => kw5
)
[notes] => asdf
)
I have been told that this is wrong, and will throw errors.
I was wondering if there is a better way to achieve this, if it is actually wrong, and if there is an easier way to do this.
With error reporting enabled, I have not run across anything that tells me what I am doing is wrong.
Thanks for your time.
It's wrong in pure OOP terms because you're using PHP's (somewhat unusual) ability to add arbitrary attributes to instantiated objects via your setData method.
What you should be doing - to achieve the goals of encapsulation and data validation - is something like this :
class AddWebsites {
private $name;
private $pageRank;
// etc
// Setters
public function setName(value) {
// you can put validation logic in here
this->name = value;
}
public function setPageRank(value) {
// you can put validation logic in here
this->pageRank = value;
}
// etc
// getters
public function getName() {
return this->name;
}
public function getPageRank() {
return this->pageRank;
}
}
This is using "Getters" and "Setters".
You could however have your members as public then you wouldn't need the getters
One of things i can notice is passing field name in function parameter is not an good idea. Reason behind that is if you by mistake pass wrong field name then php will create one more field for that object.
So if you are having multiple objects of same class some will have that field some will not. This leads to inconsistency.
So I feel this is not correct thing to do as you are not suppose to create properties of class pbject dynamically.
Ideal way is to have different getter and setter functions for each field and fields should be private in scope, so that you/developer will not not able to create new fields by mistake.

PHP: What is this object type, and how do I add to it without causing warnings

I've encountered this here and there, and I always worked around it, but I've just gotta know.
Is this an array, an object, or ??? (Let's say I got this via var_export($co))
stdClass::__set_state(array(
'name' => 'Acme Anvil Corp.',
))
Most importantly, how can I add a value to it?
Let's say I want to add a value, like $co->owner = 'Wiley Coyote'. That always throws a warning.
How?
What?
The curiosity is just KILLING me :)
[EDIT]
As a clarification, I guess the root of my question would be "How do I add variables to the object without triggering Warnings?"
Here's the warning I always get:
A PHP Error was encountered
Severity: Warning
Message: Attempt to assign property of non-object
And a var_dump($co) yields: (currently done in a loop, if it's pertinent)
object(stdClass)#185 (1) {
["name"]=>
string(16) "Acme Anvil Corp."
}
[/EDIT]
$co is object of type stdClass. You can create one and add as many properties as you want without any warning:
$obj = new stdClass();
$obj->prop1 = 'val1';
$obj->prop2 = 3.14;
The warning you are facing is most probably Creating default object from null value, meaning that you are trying to assign property to uninitialized variable.
You should not try running the code produced by var_export for stdClass objects, since they don't have method __setState.
Not too sure about your error - but the stdClass class is a default in built class to PHP.
It's basically an empty object
You should be able to set properties with $class->foo = 'bar'
If you're not talking about stdClass - but the fact you're getting an array in var_export - it's simply a representation of the internal object state .
class Foo
{
private $bar = 'baz';
}
$foo = new Foo();
var_export($foo);
Should give output similar to
Foo::__set_state(array(
'bar' => 'baz',
))
The warning you get may be due to the property being protected/private.
var_dump() generally tells you about the access modifiers on internal objects better than var_export()
Can you clarify what the warning you get is and what the question is regarding, and I'll edit up my answer to make it relevant to what you're asking
var_export() prints executable PHP code able to recreate the variable exported.
When encountering objects, they are always exported as a call to their static magic method __set_state(). Compared to other magic methods, this method does not really have much magic in it besides the fact that var_export guarantees to create static calls with this method, so it is simply just the agreed upon name for such a method.
The __set_state() method of a class should return an instance of the object with all exported properties set to the values given as the array parameter.
Example:
class Foo {
public $bar;
public static function __set_state($params) {
$obj = new Foo;
foreach ($params as $key => $value) {
$obj->$key = $value;
}
return $obj;
}
}
$foo = new Foo;
$foo->bar = "Hello World";
var_export($foo);
This prints something like:
Foo::__set_state(array('bar' => 'Hello World'));
If you execute this and save it to a variable again:
$x = Foo::__set_state(array('bar' => 'Hello World'));
it will recreate the object with all given properties set.
It does not work with stdClass objects, because they don't have a __set_state() method.
See http://de3.php.net/manual/en/language.oop5.magic.php#object.set-state for more info.
Update:
This code does not trigger any warnings:
ini_set('display_errors', 1);
error_reporting(E_ALL | E_STRICT);
$co = new stdClass();
$co->name = 'Acme Anvil Corp.';
var_export($co);
$co->owner = 'Wiley Coyote';

PHP Error, is it resolvable, or a language bug?

Given the following code
$c= new SoapClient('http://www.webservicex.net/CurrencyConvertor.asmx?WSDL');
$usa = "USD";
$eng = "GBP";
doing a __getTypes on the client gives me
Array ( [0] => struct ConversionRate { Currency FromCurrency; Currency ToCurrency; } [1] => string Currency [2] => struct ConversionRateResponse { double ConversionRateResult; } )
if i then do
$calculation = $c->ConversionRate($usa, $eng);
and print calculation i get an error about
Catchable fatal error: Object of class stdClass could not be converted to string
Is there a specific way i should be printing this out, or i it a bug, from researching / googling many people seem to have a problem but i cant find a suitbale solution, other than downgrading php, which isnt a solution for me as i am doing this as homework and its running off of a college server
I'm guessing the return type from that function is not a string (or anything with __toString defined). Normally instances of stdClass will have one or more properties which will be of use to you.
Try doing something like:
print_r($calculation)
That should tell you what the object has on it, and what it is you might want to do with it. I'd guess you'd want to be printing some property off there along the lines of (example):
echo $calculation->result;
Try passing the parameters as an array:
$parameters = array('FromCurrency' => "USD",
'ToCurrency' => "GBP");
$calculation = $soapClient->ConversionRate($parameters)
var_dump($calculation);
var_dump() could highlight that your result is an object and the double could be a member of that object.
Example:
$calculation->ConversionRateResult;

PHP architecture, and pass-by-reference vs pass-by-value

Seeking suggestions from PHP architects!
I'm not terribly familiar with PHP but have taken over maintenance of a large analytics package written in the language. The architecture is designed to read reported data into large key/value arrays, which are passed through various parsing modules to extract those report parameters known to each of those modules. Known parameters are removed from the master array, and any leftovers which were not recognized by any of the modules, are dumped into a kind of catch-all report showing the "unknown" data points.
There are a few different methods being used to call these parser modules, and I would like to know which if any are considered to be "proper" PHP structure. Some are using pass-by-reference, others pass-by-value, some are functions, some are objects. All of them modify the input parameter in some way.
A super-simplified example follows:
#!/usr/bin/php
<?php
$values = Array("a"=>1, "b"=>2, "c"=>3, "d"=>4 );
class ParserA {
private $a = null;
public function __construct(&$myvalues) {
$this->a = $myvalues["a"];
unset($myvalues["a"]);
}
public function toString() { return $this->a; }
}
// pass-by-value
function parse_b($myvalues) {
$b = $myvalues["b"];
unset($myvalues["b"]);
return Array($b, $myvalues);
}
// pass-by-reference
function parse_c(&$myvalues) {
echo "c=".$myvalues["c"]."\n";
unset($myvalues["c"]);
}
// Show beginning state
print_r($values);
// will echo "1" and remove "a" from $values
$a = new ParserA($values);
echo "a=".$a->toString()."\n";
print_r($values);
// w ill echo "2" and remove "b" from $values
list($b, $values) = parse_b($values);
echo "b=".$b."\n";
print_r($values);
// will echo "3" and remove "c" from $values
parse_c($values);
print_r($values);
?>
The output will be:
Array
(
[a] => 1
[b] => 2
[c] => 3
[d] => 4
)
a=1
Array
(
[b] => 2
[c] => 3
[d] => 4
)
b=2
Array
(
[c] => 3
[d] => 4
)
c=3
Array
(
[d] => 4
)
I'm really uncomfortable having so many different call methods in use, some of which have hidden effects on the call function parameters using "&pointer"-style functions, some requiring the main body to write their output, and some writing their output independently.
I would prefer to choose a single methodology and stick with it. In order to do so, I would also like to know which is most efficient; my reading of the PHP documentation indicates that since it uses copy-on-write, there shouldn't be much performance difference between using pointers to vs passing the object directly and re-reading a return value. I would also prefer to use the object-oriented structure, but am uncomfortable with the hidden changes being made to the input parameter on the constructor.
Of the three calling methods, ParserA(), parse_b(), and parse_c(), which if any is the most appropriate style?
I'm not really an expert in PHP but from my experience passing by value is better. This way code won't have side effects and that mean it will be easier to understand and maintain and do all sorts of crazy things on it, like using it as callback for map function. So I'm all for parse_b way of doing things.
FYI: In PHP, objects are always passed by reference, no matter what. Also if you have an array with objects and scalar values in it, the scalar values are passed by value, but the objects by reference.
As a general rule in PHP, do not use references unless you really have to.
references in PHP are also not what most people expect them to be:
"References in PHP are a means to access the same variable content by different names. They are not like C pointers; instead, they are symbol table aliases.""
see also: php.net: What References Are
So in short:
The proper way of handling this PHP is using creating an object that passes the variables around by value or manipulating the array with array_map (array_map allows you to apply a callback function to the elements an array.)
I would vote against the methods proposed in general, but of them, I think parse_b has the best idea.
I think it would be better design to wrap the "data" array in a class that could let you "pop" a key out of it easily. So the parser ends up looking like:
class ParserA {
private $a = null;
public function __construct(My_Data_Class $data) {
$this->a = $data->popValue("a");
}
public function toString() { return $this->a; }
}
And a sample implementation
class My_Data_Class {
protected $_data;
public function __construct(array $data) {
$this->_data = $data;
}
public function popValue($key) {
if (isset($this->_data[$key])) {
$value = $this->_data[$key];
unset($this->_data[$key]);
return $value;
}
}
}

Categories