How does $this->print = $render work in Drupal? - php

I understand that this is object oriented, and that you are essentially passing the value of the [print] cell from the [this] array into a separate variable called $render.
But, does this automatically cause the information to be displayed on screen?
see code for reference:
public function setPrintRender(array $render) {
$this->print = $render;
}
Or is this information going to respectfully stay quietly in that variable until some other process grabs it and then displays it on the screen?
UPDATE
So, maybe this part of the code is what actually prints the info?
public function build() {
$output = array();
foreach ($this->panes as $pane) {
$output[$pane->getId()] = $this->buildPane($pane);
}
if ($render = $this->pos->getState()->getPrintRender()) {
$output[] = array(
'#prefix' => '<div class="element-invisible"><div class="pos-print">',
'#suffix' => '</div></div>',
'print' => $render,
);
}
return $output;
}

No, it doesn't cause it to be rendered.
A variable $render is being passed into setPrintRender() which then sets it to an internal variable called $this->print.
This is known as a setter. You will generally have a getter and setter method for every publicly available variable.

Related

Why function parameters doesn't work with class

I created a class. The code is below
class Another {
public $error = array();
public function set_error( $key, $value )
{
if ( isset( $key ) ) {
$sanitizedKey = sanitize_key( $key );
$this->error[ $sanitizedKey ] = wp_json_encode( $value );
return $this->error;
}
}
public function get_error( $id )
{
if ( ! is_null( $id ) ) {
return $this->error[ $id ];
}
}
public function print_error()
{
if ( $this->error ) {
foreach ($this->error as $key => $value) {
$decodeJson = json_decode( $value );
?>
<div class="ud-error">
<p class="ud-error-<?php echo $key; ?>">
<?php echo __( $decodeJson, 'ud-for-edd' ); ?>
</p>
</div>
<?php
}
}
}
}
If I invoke it in the following way it works. It echos the content as expected.
$error = new Another();
$error->set_error('gg', 'hhhh');
$error->print_error();
But if I use it with function then it doesn't work as expected. Do I have to pass parameters by reference or any other? The following way it doesn't work
function create_error($id, $val) {
$errr = new Another();
return $errr->set_error($id, $val);
}
create_error('raa', 'raashid');
$error = new Another();
$error->print_error();
I am confused about why this doesn't work. Any clue. Thanks in advance.
Steps I want the code to perform:
Create a class with 3 methods, set_error, get_error, and print_error
Then invoke the class inside the function. The function will accept two parameters because inside the class the set_error() method accepts two parameters.
In order to print the error, I will instantiate the class and call the print_error() method.
In case, if I have to print the new error. I will just call the create_error() function to do this for me. Because the function needs 2 parameters. The arguments supplied to the function must be supplied as arguments to the set_error() method.
I hope the list helps.
Update:
If I use a global variable then it works. Below is working.
$customError = new Another();
function create_error($id, $val) {
global $customError;
$customError->set_error($id, $val);
}
create_error('raa', 'rashid');
$customError->print_error();
Thanks, #roggsFolly and #El_Vanja. By understanding your tips I was able to solve the error. If there is anything wrong with the code I just said worked. Please point out.
The object you instantiate inside the function is not the same one you try and print the error message from.
First the object you instantiate inside the function scope is not visible outside the function.
function create_error($id, $val) {
$errr = new Another();
return $errr->set_error($id, $val);
}
create_error('raa', 'raashid');
// this instantiates a seperate Another object from the one
// you created in the function
$error = new Another();
// this tries to print from the new object taht has no message stored in it yet
$error->print_error();
To instantiate the object inside a function scope and then use that object outside the function scope you must pass that object back to the caller of the function
function create_error($id, $val) {
$errr = new Another();
$errr->set_error($id, $val);
return $errr; // this returns the object
}
$error = create_error('raa', 'raashid');
// now you can use its methods to print the stored message
$error->print_error();
Update as per your additional Information
A couple of things I think you may be getting confused about.
Each time you do $var = new ObjectName; you are creating a brand new instance of that class. This new instance has no knowledge about any other instances of that class that may or may not have been created before or may be created after that point. And more specifically to your problems, it does not have access to the properties of another version of that object.
You are I believe missing the concept of variable scope. The Object you create inside that function, will only actually exist while the function is running. Once the function completes anything created/instantiated wholly within that function is DESTROYED ( well in truth it is just no longer accessible ) but to all intent and purpose it is destroyed. you therefore cannot expect to be able to address it outside the scope of the function.
If you want the Object you instantiate within the function to be usable outside the function, you must pass a reference to that object out of the function to the calling code. This passes that reference into the scope of the calling code and keeps the object alive, global scope in your case, but that might be another function or even another object. That allows you access to that instantiation and any properties that were set within it.

How to keep PHP object alive (or in memory) between AJAX calls

I have the following class definition:
class DatasegmentationController
{
public function indexAction()
{
$options['permissions'] = array(
'canDelete' => false,
'canEdit' => false
);
if ($this->getRequest()->isXmlHttpRequest()) {
$table = $this->getRequest()->getParam('table');
if ($table !== '' && $table !== null) {
$utilStr = new UtilString();
// This is a patch because class and tables names does not match
// so far it only happens with company and this is only for
// instantiate the proper class dynamically
$param_table = $table;
$table = $table === 'companies' ? 'company' : $table;
$classObj = strpos($table, '_') !== false ? $utilStr->stringToCamelCase($table, '_') : $utilStr->stringToCamelCase($table);
$className = new $classObj();
$module_map = $field_map[$param_table];
/** #var $module_map array */
$fields = [];
foreach ($module_map as $key => $value) {
$fields[] = [
'id' => $key,
'text' => $key
];
}
$conditions = json_decode($this->_request->getParam('conditions'), true);
$dynDataGridName = "DataSegmentation{$this->classObj}Grid";
$dynMethodName = "get{$this->classObj}GridModel";
$gridObj = new $dynDataGridName(
$this->className->$dynMethodName($conditions),
$this->view->users_id,
"{$table}_list",
"{$table}.{$table}_id",
'/datasegmentation/index',
'editor',
$options
);
return $this->_helper->json([
'fields' => $fields,
'grid' => $gridObj->getGridJs()
]);
}
if (isset($classObj, $className, $gridObj)) {
$page = $this->_request->getParam('page', 1);
$limit = $this->_request->getParam('rows', 20);
$col = $this->_request->getParam('sidx', 1);
$order = $this->_request->getParam('sord', 0);
$search = $this->_request->getParam('val', null);
echo $gridObj->getData($page, $limit, $col, $order, $search);
}
}
}
}
What the code above does is the following:
The URL http://localhost/datasegmentation is called
The view render a select element (modules) with options
When the select#modules is changed I sent it's value as part of the URL so the next AJAX call becomes: http://localhost/datasegmentation?table=companies (for example)
The indexAction() function then perform what is on the conditional for when $table is not empty or is not null
Among all those stuff it tries to generate everything dynamically as you may notice in code.
One of those stuff is a dinamic grid ($gridObj) which has a AJAX call to the same indexAction() but without parameters for populate the data after gets rendered
After the grid gets rendered at the view it makes the AJAX call and again the indexAction() is called and it skip the conditional for the table because the parameter is not set and tries the second conditional but surprise it fails because the objects that code needs to work are gone.
Having that scenario my questions are:
How do I keep the object alives between AJAX calls? Storing in a session var? Any other workaround?
If the answer is store them in a session var, is it recommendable? What about the answers on this, this and this among others?
How would you handle this?
The problem
The second AJAX call is the one adding data to the grid and it relies on the dynamic parameters. This is what I need to solve for make this to work.
I don't know if this is useful at all but this is being tested and develop on top of Zend Framework 1 project using PHP 5.5.x.
How do I keep the object alives between AJAX calls? Storing in a
session var? Any other workaround?
Storing the data in a session var
Storing the data in a file, with a client ID (could be a login, random, IP, etc)
Storing the data in a database.
If the answer is store them in a session var, is it recommendable?
What about the answers on this, this and this among others?
If you are storing critical data, use end to end encryption, SSL, HTTPS.
How would you handle this?
Using session variables.

Can an Abstract Class in PHP Have a Default Method?

I've most certainly got something very basic wrong here.
Here is the code that is part of my Abstract Class:
private $outarray = null;
public function add_to_array($ahref, $docname, $description) {
$row = array('ahref' => $ahref, 'docname' => $docname, 'description' => $description);
if (!isset($this->outarray)) {
$this->outarray = array();
}
array_push($this->outArray, $row);
}
When I step through the code, though, the outArray remains null. It is never created and never populated.
I'm still green with PHP, but this help doc seems to leave me believing that this is OK to do:
http://www.php.net/manual/en/language.oop5.abstract.php
...particularly where they are declaring the Common method printOut() that performs some action.
I've got 5 elements I am trying to populate outArray with, but each of the 5 times I circle into this function, I come out with outArray being NULL.
Variables are case sensitive. You have in one place $this->outarray and in array_push you have $this->outArray
Ugh.
PHP is case sensitive, but it does not complain about it because it was assuming I had another variable declared on the fly.
Correct way:
public function add_to_array($ahref, $docname, $description) {
$row = array('ahref' => $ahref, 'docname' => $docname, 'description' => $description);
if (!isset($this->outarray)) {
$this->outarray = array();
}
array_push($this->outarray, $row);
}

$this in Array of Closures in Class

Say there is a class for objects, let's use a User as an example. The User class contains it's own Rules to validate it's data before submitting. Before saving to the database, the Rules will be checked and any errors will be returned. Otherwise the update will run.
class User extends DBTable // contains $Rules, $Data, $Updates, and other stuff
{
public __construct($ID)
{
parent::__construct($ID);
// I'll only list a couple rules here...
$this->Rules['Email'] = array(
'Empty' => 'ValidateEmpty', // pre-written functions, somewhere else
'Invalid' => 'ValidateBadEmail', // they return TRUE on error
'Duplicate' => function($val) { return existInDatabase('user_table', 'ID_USER', '`Email`="'. $val .'" AND `ID_USER`!='. $this->ID);}
);
$this->Rules['Password'] = array(
'Empty' => 'ValidateEmpty',
'Short' => function($val) { return strlen($val) < 8; }
);
this->Rules['PasswordConfirm'] = array(
'Empty' => 'ValidateEmpty',
'Wrong' => function($val) { return $val != $this->Updates['Password']; }
);
}
public function Save(&$Errors = NULL)
{
$Data = array_merge($this->Data, $this->Updates);
foreach($this->Rules as $Fields => $Checks)
{
foreach($Checks as $Error => $Check)
{
if($Check($Data[$Field])) // TRUE means the data was bad
{
$Errors[$Field] = $Error; // Say what error it was for this field
break; // don't check any others
}
}
}
if(!empty($Errors))
return FALSE;
/* Run the save... */
return TRUE; // the save was successful
}
}
Hopefully I posted enough here. So you'll notice that in the Duplicate error for Email, I want to check that their new email does not exist for any other user excluding themselves. Also PasswordConfirm tries to use $this->Updates['Password'] to make sure they entered the same thing twice.
When Save is run, it loops through the Rules and sets any Errors that are present.
Here is my problem:
Fatal error: Using $this when not in object context in /home/run/its/ze/germans/Class.User.php on line 19
This error appears for all closures where I want to use $this.
It seems like the combination of closures in an array and that array in a class is causing the problem. This Rule array thing works fine outside of a class (usually involving "use") and AFAIK closures are supposed to be able to use $this in classes.
So, solution? Work-around?
Thanks.
The problem is with the Wrong validator. The validation method is called from here:
if($Check($Data[$Field])) // TRUE means the data was bad
This call is not made in an object context (the lambda is not a class method). Therefore $this inside the body of the lambda causes an error because it only exists when in an object context.
For PHP >= 5.4.0 you can solve the problem by causing the capture of $this:
function($val) { return $val != $this->Updates['Password']; }
In this case you will be able to access Updates no matter what its visibility is.
For PHP >= 5.3.0 you need to make a copy of the object reference and capturing that instead:
$self = $this;
function($val) use($self) { return $val != $self->Updates['Password']; }
In this case however, you will only be able to access Updates if it is public.

PHP: Class property chaining in variable variables

So, I have a object with structure similar to below, all of which are returned to me as stdClass objects
$person->contact->phone;
$person->contact->email;
$person->contact->address->line_1;
$person->contact->address->line_2;
$person->dob->day;
$person->dob->month;
$person->dob->year;
$album->name;
$album->image->height;
$album->image->width;
$album->artist->name;
$album->artist->id;
etc... (note these examples are not linked together).
Is it possible to use variable variables to call contact->phone as a direct property of $person?
For example:
$property = 'contact->phone';
echo $person->$property;
This will not work as is and throws a E_NOTICE so I am trying to work out an alternative method to achieve this.
Any ideas?
In response to answers relating to proxy methods:
And I would except this object is from a library and am using it to populate a new object with an array map as follows:
array(
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
);
and then foreaching through the map to populate the new object. I guess I could envole the mapper instead...
If i was you I would create a simple method ->property(); that returns $this->contact->phone
Is it possible to use variable variables to call contact->phone as a direct property of $person?
It's not possible to use expressions as variable variable names.
But you can always cheat:
class xyz {
function __get($name) {
if (strpos($name, "->")) {
foreach (explode("->", $name) as $name) {
$var = isset($var) ? $var->$name : $this->$name;
}
return $var;
}
else return $this->$name;
}
}
try this code
$property = $contact->phone;
echo $person->$property;
I think this is a bad thing to to as it leads to unreadable code is is plain wrong on other levels too, but in general if you need to include variables in the object syntax you should wrap it in braces so that it gets parsed first.
For example:
$property = 'contact->phone';
echo $person->{$property};
The same applies if you need to access an object that has disalowed characters in the name which can happen with SimpleXML objects regularly.
$xml->{a-disallowed-field}
If it is legal it does not mean it is also moral. And this is the main issue with PHP, yes, you can do almost whatever you can think of, but that does not make it right. Take a look at the law of demeter:
Law of Demeter
try this if you really really want to:
json_decode(json_encode($person),true);
you will be able to parse it as an array not an object but it does your job for the getting not for the setting.
EDIT:
class Adapter {
public static function adapt($data,$type) {
$vars = get_class_vars($type);
if(class_exists($type)) {
$adaptedData = new $type();
} else {
print_R($data);
throw new Exception("Class ".$type." does not exist for data ".$data);
}
$vars = array_keys($vars);
foreach($vars as $v) {
if($v) {
if(is_object($data->$v)) {
// I store the $type inside the object
$adaptedData->$v = Adapter::adapt($data->$v,$data->$v->type);
} else {
$adaptedData->$v = $data->$v;
}
}
}
return $adaptedData;
}
}
OOP is much about shielding the object's internals from the outside world. What you try to do here is provide a way to publicize the innards of the phone through the person interface. That's not nice.
If you want a convenient way to get "all" the properties, you may want to write an explicit set of convenience functions for that, maybe wrapped in another class if you like. That way you can evolve the supported utilities without having to touch (and possibly break) the core data structures:
class conv {
static function phone( $person ) {
return $person->contact->phone;
}
}
// imagine getting a Person from db
$person = getpersonfromDB();
print conv::phone( $p );
If ever you need a more specialized function, you add it to the utilities. This is imho the nices solution: separate the convenience from the core to decrease complexity, and increase maintainability/understandability.
Another way is to 'extend' the Person class with conveniences, built around the core class' innards:
class ConvPerson extends Person {
function __construct( $person ) {
Person::__construct( $person->contact, $person->name, ... );
}
function phone() { return $this->contact->phone; }
}
// imagine getting a Person from db
$person = getpersonfromDB();
$p=new ConvPerson( $person );
print $p->phone();
You could use type casting to change the object to an array.
$person = (array) $person;
echo $person['contact']['phone'];
In most cases where you have nested internal objects, it might be a good time to re-evaluate your data structures.
In the example above, person has contact and dob. The contact also contains address. Trying to access the data from the uppermost level is not uncommon when writing complex database applications. However, you might find your the best solution to this is to consolidate data up into the person class instead of trying to essentially "mine" into the internal objects.
As much as I hate saying it, you could do an eval :
foreach ($properties as $property) {
echo eval("return \$person->$property;");
}
Besides making function getPhone(){return $this->contact->phone;} you could make a magic method that would look through internal objects for requested field. Do remember that magic methods are somewhat slow though.
class Person {
private $fields = array();
//...
public function __get($name) {
if (empty($this->fields)) {
$this->fields = get_class_vars(__CLASS__);
}
//Cycle through properties and see if one of them contains requested field:
foreach ($this->fields as $propName => $default) {
if (is_object($this->$propName) && isset($this->$propName->$name)) {
return $this->$propName->$name;
}
}
return NULL;
//Or any other error handling
}
}
I have decided to scrap this whole approach and go with a more long-winded but cleaner and most probably more efficient. I wasn't too keen on this idea in the first place, and the majority has spoken on here to make my mind up for me. Thank for you for your answers.
Edit:
If you are interested:
public function __construct($data)
{
$this->_raw = $data;
}
public function getContactPhone()
{
return $this->contact->phone;
}
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
}
if (isset($this->_raw->$name)) {
return $this->_raw->$name;
}
return null;
}
In case you use your object in a struct-like way, you can model a 'path' to the requested node explicitly. You can then 'decorate' your objects with the same retrieval code.
An example of 'retrieval only' decoration code:
function retrieve( $obj, $path ) {
$element=$obj;
foreach( $path as $step ) {
$element=$element[$step];
}
return $element;
}
function decorate( $decos, &$object ) {
foreach( $decos as $name=>$path ) {
$object[$name]=retrieve($object,$path);
}
}
$o=array(
"id"=>array("name"=>"Ben","surname"=>"Taylor"),
"contact"=>array( "phone"=>"0101010" )
);
$decorations=array(
"phone"=>array("contact","phone"),
"name"=>array("id","name")
);
// this is where the action is
decorate( $decorations, &$o);
print $o->name;
print $o->phone;
(find it on codepad)
If you know the two function's names, could you do this? (not tested)
$a = [
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
];
foreach ($a as $name => $chain) {
$std = new stdClass();
list($f1, $f2) = explode('->', $chain);
echo $std->{$f1}()->{$f2}(); // This works
}
If it's not always two functions, you could hack it more to make it work. Point is, you can call chained functions using variable variables, as long as you use the bracket format.
Simplest and cleanest way I know of.
function getValueByPath($obj,$path) {
return eval('return $obj->'.$path.';');
}
Usage
echo getValueByPath($person,'contact->email');
// Returns the value of that object path

Categories