Accessing an object property without knowing the name? - php

I'm writing a PHP script that accesses two different data objects - one is backed by a DB, the other contains session data. They both contain the same fields - i.e. the data that is in the session object will make its way into the DB once it is validated.
I'm trying to write a function that will first check the db-backed object for a value, then check the session-data-backed object for a value. It's not working like I quite expect, however. Here is what I have.
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->$field ? $app->$field : $data->$field ? $data->$field : '';
}
?>
I'd like to be able to call the function like this:
<input type="text" value="<?php echo check_cache('address'); ?>" />
...but the function always returns nothing. When I replace the function call with the actual inline code, substituting $field with the field name I want, it works. What am I missing?

You should ensure that $app and $data exist within the scope of the function (potential by passing them as parameters, I'll leave that to you). Try this:
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->${$field} ? $app->${$field} : $data->${$field} ? $data->${$field} : '';
}
?>
You should probably extend this though to ensure that $app->$$field exists, etc.
Edit:
This is pretty wrong, as pointed out by bob-the-destroyer in the comments. $app->$field is all you need. Just make sure the scope is right, and the members you want aren't private. Apologies.

You can try something like this:
<?php
function check_cache($field){
global $app, $data;
// $App is the DB backed object, $data is the session object
$av = eval("\$app->".$field);
$dv = eval("\$data->".$field);
return $av ? $av : $dv ? $dv : '';
}
?>

I'd be tempted to say it's a scope issue, $app + $data don't exist inside the function, only outside. Extend the function to take two extra params, and pass in the $app + $data objects that way.
Addendum
This won't entirely fix the problem, you'll still need to access the fields of the object, depending on your class structure, if it's properties are private, will make some of the alternate answers not work, as the field won't be accessible via the obj->field syntax.

Related

Laravel Blade Directives recieve object instead of var

Short version of the question is:
Is there a way to send the actual $object to the custom directive and reuse it in a view?
Details:
I created a new directive, which is:
#object_actions($object)
This $object is actually an object, and it can be named anything. The directive returns a view with html code, built as a tool to be used everywhere on the site.
So let's say I have a $post. That post can have $post->like(). It is basically a model, now just like it can be $post it can be $photo as well, both are models.
Now let's say I am in the photos view, with that #object_actions I can easily embed the layouts.objects.actions.blade.php views like that:
#object_actions($photo)
Same in posts views, I can
#object_actions($post)
Both are models again, or they can be anything object with methods, etc.
I could simply use #include('layouts.objects.actions') and it works. But that's not practical at all, since the layouts.objects.actions has this html code:
<span data-liked="<?php echo $object->isLiked() ? 'true' : 'false';?>">
<span class="likes-count">{{$object->likes()->count()}}</span>
</span>
So as you can see, it uses $object as the var name or object name, including this in posts view, or any view where the actual var name is either $post, $photo, $comment is never $object, and doing something like $object = $post, $object = $photo is kind of messy.
I thought I could create a directive like
Blade::directive('object_actions', function ($object) {
// dd($object) -- returns a string and not the model
// Include the view here
});
However, $object is always a $string, a dd before the return dd($object) returns a string, therefore the actions view throws an error call to a member function xxx() on string.
dd on
#object_actions($photo) // returns '$photo
#object_actions($post) // returns '$post'
I want the real object I sent from the original view.
So is there a way to send the actual $object to the custom directive and reuse it in a view?
The solution I found was this:
// AppServiceProvider
Blade::directive('actions', function ($object) {
return "<?php echo \$__env->make('blocks.objects.actions', ['object'=>{$object}])->render(); ?>";
});

PHP - Reference a StdClass Object from a variable?

Maybe I am searching wrong, but I'm unable to find how to specify an Object Class in a string. Example:
I keep a list of json sites in a database, perform a foreach loop to retrieve a specific item from each site. However, each site has a different structure.
SITE 1: $result->Food->Price->Today;
SITE 2: $result->Pizza->Slice->discount;
I am trying to keep "$result->Pizza->Slice->discount" in a variable (specified in database) and return it from the class.
Sounds easy, but I'm new to class objects and all I find is how to create an object from an array.
Store this value into your database:
serialize(['Pizza', 'Slice', 'discount']);
When reading the value, unserialize it:
unserialize($value_from_db);
To retrieve the value from the JSON object use this simple function:
function retrieve_value($object, $trail) {
foreach ($trail as $name)
if (isset($object->$name))
$object = $object->$name;
else
throw new Exception("Object does not have the property $name");
return $object;
}
So you have something like this:
$value = retrieve_value(json_decode($json), unserialize($db_value));
Do not use eval(), because it is evil.
A dirty way to do this would be to json encode and decode.
$array = array([Your array]);
$obj = json_decode(json_encode($array));
Haven't tested the code though.
I guess you could store the pathing inside the database then use "eval()" with the path on the object. Just make sure you know no one can alter the pathing because eval is dangerous with un-sanitized code!
eval("\$value = \$result->{$pathvar};");
I didn't test that, but something of the sort. Of course $pathvar would be the value of the path coming from the database, whatever variable that's stored in.

set models based on condition in cakephp queries

This is probably very easy to do, but I can't seem to get my head around it right now. Let's say in a component in a cakephp application, I have a variable my_model, which contains the model of the corresponding controller that is currently using the component like:
function TestComponent extend Object
{
var $my_model; // can be either User, or Person
function test()
{
$myModelTemp = $this->my_model;
$model = $myModelTemp != 'User' ? $myModelTemp.'->User' : 'User';
$this->$model->find('all');
}
}
As you can see above in my function test() what I'm trying to do is call the correct model based on the value of my_model. So based on the condition, my query will be either:
$this->Person->User->find('all');
Or
$this->User->find('all');
When I do it like I did above, I get an error saying Fatal error: Call to a member function find() on a non-object. In order words, that error means Person->User is not an object (so, it is considered as a string).
What you're saying could be true, however, it can refer to any part of the call.
So either Person or User could be invalid, or together they causes the error. Hard to say.
Try dumping the individual objects using var_dump();
So try:
<?php
echo "<pre>";
var_dump(is_object($this->Person));
var_dump(is_object($this->User));
echo "</pre>";
?>
to determine where you're code goes wrong.
To be clear, that return value needs to be true for it to be an object.
The one that returns false is the likely culprit.
Should your question refer to the correct way to reference an object, an object is basically an array. For example:
<?php
$obj = (object) array("this", "my_function");
?>
The above example casts the array as an object. However, using multiple layers might prove to be more difficult than you'd expect.
Generally, it looks like you might be going about this all wrong. Obviously you want the models to be dynamic, but then you're hard-coding things which defeats the whole point of it being dynamic in the first place.
It also seems like you might be violating the principals of CakePHP and MVC by doing all this in a component. I'm not sure this component should really be manipulating models or assuming which models are currently in use.
However, if you want to evaluate a string as an actual object, you can wrap it in { ... } (this is valid standard PHP syntax, not Cake-specific code).
Try this:
$modelName = $this->my_model;
$model = ($modelName != 'User') ? $this->{$modelName}->User : $this->User;
$model->find('all');
Now, if this doesn't work or you get an error saying it can't find the model(s) you need to ensure the models are actually loaded and initialised in the current scope.

How to "globalize" PHP variables?

I have a page named ChangeApprovalInfo.php - It has a function called Row_Rendered as follows;
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$RecordOwner = $this->RequestUser->CurrentValue;
echo $RecordOwner;
}
Echoing $RecordOwner gets me the data I will need for a sql query on another page....
I have another page called ChangeApprovalEdit.php - This page has
<?php include_once "ChangeApprovalinfo.php" ?>
at the top of the file.
ChangeApprovalEdit.php has a function where I need the $RecordOwner variable as defined in ChangedApprovalInfo.php
If I add "echo $RecordOwner" on the ChangeApprovalEdit.php page, I get an error saying it's an unknown variable. My understanding is that I need to "make it global" or some such business. I know very little about PHP and the pages I am editing are long and complex. (to me, at least)
What do I need to do? I know that the information I have provided might not be enough to answer the question. I don't know enough to even know exactly what I need to ask. If more information is needed, I will edit and follow up.
pastebin of the files
ChangeApprovalInfo.php = http://pastebin.com/bSRM1wwN
ChangeApprovalEdit.php = http://pastebin.com/AStG9pqb
EDIT:
Changing Row_Rendered to this seems to be more effective. I'm having trouble seeing WHERE I can later echo this variable... but I'm getting somewhere with this...
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
Don't echo variables from functions, which just outputs them to the standard output. return them from the function so you can use the value elsewhere as well.
function Row_Rendered() {
$RecordOwner = $this->RequestUser->CurrentValue;
return $RecordOwner;
}
Then instead of
$obj->Row_Rendered();
use
echo $obj->Row_Rendered();
and if you want to use the value elsewhere, use
$value = $obj->Row_Rendered();
You can do a couple of things:
First, you can return $RecordOwner from the function, and store its value in a variable. This method is usually preferred.
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$RecordOwner = $this->RequestUser->CurrentValue;
echo $RecordOwner;
return $RecordOwner;
}
// Store it in a variable when calling the function.
$RecordOwner = Row_Rendered();
Or, you can make it global inside the function:
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
echo $GLOBALS['RecordOwner'];
}
You can use the $GLOBALS superglobals array, like this:
function Row_Rendered() {
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
However, you should not do that. Instead, refactor your application so that the view in ChangeApprovalinfo.php just contains a function, which is then called with the appropriate parameters.
EDIT: Chaning Row_Rendered to this seems to be more effective. I'm having trouble seeing WHERE I can later echo this variable... but I'm getting somewhere with this...
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
I feel compelled to write another answer to this update. Let me demonstrate the use of globals as seen from outside that function:
$obj->Row_Rendered();
$obj->foobar();
echo $GLOBALS['RecordOwner'];
Quick, what will be echoed and where does that value come from? Well, it depends on what $obj-foobar() does. Maybe it changes the global variable. Maybe it doesn't. Who knows if the variable has been set at all? How would you trace back what happened exactly without adding a debug line after every single function call?
And that's just three lines of code. Imagine that in an application of any complexity.
Now, the same thing if I return the value from Row_Rendered:
$owner = $obj->Row_Rendered();
$obj->foobar();
echo $owner;
If the Row_Rendered method is behaving as it should (returning the owner), this code is very predictable. If you do not follow this pattern, you'll have a hell of a time getting anything done when the application grows to any halfway complex size.
Set the variable as global from within the function
$my_global_var = "old value";
function doing_stuff(){
global $my_global_var; //this will use the global variable instead of creating a local one
$my_global_var = "new value";
}
echo $my_global_var;//returns "new value"

Should a method ran several times in a PHP class be cached to a class variable instead?

In PHP I know many people will use a class to SET and GET session variables, I am doing this now in many classes, I need to know if I am doing it wrong though.
So for example lets pretend I have A class that need to use this
$session->get('user_id')
Which gets this value
$_SESSION['user_id']
Now in this class if I have 15 methods and in each method I need to access this value several time, currently I am calling $session->get('user_id') 20 times in a class if it is needed 20 times, should I be setting this 1 time per class to a local variable for that class and then access it? I am not sure if it makes any difference or not, my theory is that the way I am doing it now is 20 extra function calls that could be avoided?
If my theory is correct, what would be the best way to store these values inside a class? Like a private or public or protected variable?
Thanks, sorry for any confusio, classes and objects are taking me a while to learn.
Also note that $session->get('user_id') is just 1 of many DIFFERENT variables I would need to do the same thing to as well.
UPDATE
After reading Chacha102's post about using an array() ... here is what I have tried, does this look like a good way or still can be improved a lot?
class file
<?PHP
class User
{
// Load user details into an Array
public function load_user()
{
$this->user_id = $this->session->get('user_id');
//if user ID is already set, then Load the cached urser data
if(isset($this->user_id) && $this->user_id != ''){
// set user data to an array
$this->user['user_id'] = $this->user_id;
$this->user['user_name'] = $this->session->get('user_name');
$this->user['pic_small'] = $this->session->get('pic_small');
$this->user['sex'] = $this->session->get('sex');
$this->user['user_role'] = $this->session->get('user_role');
$this->user['location_lat'] = $this->session->get('location_lat');
$this->user['location_long'] = $this->session->get('location_long');
$this->user['new_user'] = $this->session->get('new_user');
return $this->user;
}
}
}
?>
main page file
<?PHP
require 'user.class.php';
$user = new User;
// if a user_id is set into a session variable then we return an array of other user related data
$user->account = $user->load_user();
// would show the user's ID from our array
echo $user->account['user_id'];
?>
If you are doing something like this:
if($session->get('user_id')==1)
{
$prefs = get_prefs($session->get('user_id'));
$info = get_info($session->get('user_id'));
}
then I would replace it with a since local variable
$id = $session->get('user_id');
if($id == 1)
{
//.....
}
It increases clarity for one. It probably isn't a big deal to call a simple function like that over and over again, but I still wouldn't do it.
I try to reduce the number of functions I call in a single method. If you are doing something like:
$user_id = $session->get('user_id');
$name = $session->get('name');
// ... etc ...
You might just want to grab an array of all the session variables instead.
$user = $session->get_array();
echo $user['user_id'];
This reduces the function calls, and you get all the data in one fell swoop.
Just one thing on clarity, using an array of user data is probably easier to read than to create a variable for each thing ($user_name, $user_id, etc).
For accesses distributed over a number of methods, as long as you're just using the function to access the variable, I'd say stay with the function. The additional cost is minuscule, and it's better for long term maintainability.
Within the same method, you would make one function call, populating a local variable, as Chacha102 suggests.
Even if the function does resource-intensive things like database calls, I would prefer giving the function some internal caching before adding a member to your class.
Adding the variable as a member to your class doesn't really make sense in the OOP way, because it's not a logical, legitimate member of the class but just a temporary variable.

Categories