问题
I have a problem with a PHP file I'm using, and I can't seem to find a solution.
In one part of the code the value for $this->value
is set, and the value is set correctly according to my testing.
However, later in the same code $this->value
is empty.
Here's the code:
<?php
class Padd_Input_Advertisement {
protected $keyword;
protected $value;
protected $name;
protected $description;
function __construct($keyword,$name,$description='') {
$this->keyword = $keyword;
$this->value = unserialize(get_option($keyword));
$this->name = $name;
$this->description = $description;
}
public function get_keyword() {
return $this->keyword;
}
public function set_keyword($keyword) {
$this->keyword = $keyword;
}
public function get_value() {
return $this->value;
}
public function set_value($value) {
$this->value = $value;
}
public function get_name() {
return $this->name;
}
public function set_name($name) {
$this->name = $name;
}
public function get_description() {
return $this->description;
}
public function set_description($description) {
$this->description = $description;
}
public function __toString() {
$strHTML = '';
$strHTML .= '<tr valign="top">';
$strHTML .= ' <th scope="row"><label for="' . $this->keyword . '">' . $this->name . '</label></th>';
$strHTML .= ' <td>';
$strHTML .= ' <label for="' . $this->keyword. '_alt_desc">Short Description</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_alt_desc" type="text" id="' . $this->keyword . '_alt_desc" value="' . $this->value->get_alt_desc() . '" size="80" /><br />';
$strHTML .= ' <label for="' . $this->keyword. '_img_url">Image URL</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_img_url" type="text" id="' . $this->keyword . '_img_url" value="' . $this->value->get_img_url() . '" size="80" /><br />';
$strHTML .= ' <label for="' . $this->keyword. '_web_url">Website</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_web_url" type="text" id="' . $this->keyword . '_web_url" value="' . $this->value->get_web_url() . '" size="80" />';
$strHTML .= ' <br /><small>' . $this->description . '</small>';
$strHTML .= ' </td>';
$strHTML .= '</tr>';
return $strHTML;
}
}
?>
At the top in function __construct
, the value is set, and I confirmed that this happens.
However, in the bottom function function __toString
, $this->value
is blank.
Any idea what could be causing this?
EDIT
So, to recap, $this->value is set properly in the __construct function, but is blank in the __toString function.
EDIT 2
I should also mention that other variables that are set in the __construct function are working in the __toString function, like $this->keyword. It's jut $this->value that is going blank.
Edit 3 The class is called like this
$padd_options['advertisements'] = array(
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_1',
'Square Ad 1 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_2',
'Square Ad 2 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_3',
'Square Ad 3 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_4',
'Square Ad 4 (125x125)',
'The advertisement will be posted at the side bar.'
),
);
回答1:
It's because unserialized object does not keep methods if they do not know the type of the class.
When you unserialize the result of the get_keyword()
function call, if the class of the serialized object is not known, PHP will fallback on the special class __PHP_Incomplete_Class_Name
. This class is basically a dummy class : it doesn't have any method. However, it allows you to access to the attributes of the deserialized object.
So, if you want to be able to call the method of $this->value
(which is what you are doing in __toString
), you will have to include the file which declares the type of $this->value
before calling unserialize.
[edit] You can check the PHP manual for more details about the unserialization process.
回答2:
This is what's causing you problems:
$this->value = unserialize(get_option($keyword));
As some others have previously pointed out, you could update your original post with the actual declarations of the get_option()
function and the class that wraps the get_alt_desc(), get_img_url(), get_web_url()
methods.
It can be assumed that the get_option()
function returns a serialised object which implements the following public methods:
public function get_alt_desc() { /* custom code */ }
public function get_img_url() { /* custom code */ }
public function get_web_url() { /* custom code */ }
By serialising and unserialising your object you lose access to all of the above methods.
To avoid that, you need to modify the get_option()
function to return the actual object instead of a serialized representation of it.
Assuming there is a Value
class which represents your object:
class Value {
protected $alt_desc;
protected $img_url;
protected $web_url;
public function __construct($keyword) {
$this->alt_desc = $keyword[0]; // some string build around $keyword
$this->img_url = $keyword[1]; // some string build around $keyword
$this->web_url = $keyword[2]; // some string build around $keyword
}
public function get_alt_desc() {
return $this->alt_desc;
}
public function get_img_url() {
return $this->img_url;
}
public function get_web_url() {
return $this->web_url;
}
}
Then you could modify your get_option()
function to simply return an object:
function get_option($keyword) {
/*
* The code below is just an example
* This function must return an object
*/
return new Value($keyword);
}
Your Padd_Input_Advertisement
constructor should be updated as following:
function __construct($keyword,$name,$description='') {
$this->keyword = $keyword;
// removed the unserialize call as it's not necessary anymore
$this->value = get_option($keyword);
$this->name = $name;
$this->description = $description;
}
回答3:
I would have added this as a comment, but I guess I dont have the reputation points to do that quite yet.
Have you tried to turn on error reporting?
error_reporting(E_ALL);
ini_set("display_errors", 1);
回答4:
In Object oriented programming you have must have call function using $this
.
In your function __toString()
you have directly call function.
So you have called function of class like this
$this->set_name();
回答5:
Using Magaling theme from Padd Solutions, that contains the script of the question, I added at index.php in the theme's directory the following code:
/*********************************TEST***********************************/
$padd_options[ 'advertisements' ] = array( new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_468060_1', 'Banner Ad 1 (468x60)', 'The advertisement is placed beside the site name in the header.' ),
new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_468060_2', 'Banner Ad 2 (468x60)', 'The advertisement is placed just below the title in Search Result page, Categories page, Tags page, Author page, and Archives page.' ),
new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_125125_1', 'Square Ad 1 (125x125)', 'The advertisement will be posted at the side bar.' ),
new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_125125_2', 'Square Ad 2 (125x125)', 'The advertisement will be posted at the side bar.' ),
new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_125125_3', 'Square Ad 3 (125x125)', 'The advertisement will be posted at the side bar.' ),
new Padd_Input_Advertisement( PADD_THEME_SLUG . '_ads_125125_4', 'Square Ad 4 (125x125)', 'The advertisement will be posted at the side bar.' ), );
echo var_dump( $padd_options[ 'advertisements' ] );
/*********************************TEST***********************************/
And got the following result, which shows the value is an array with several other values:
array (size=6)
0 =>
object(Padd_Input_Advertisement)[286]
protected 'keyword' => string 'magaling_ads_468060_1' (length=21)
protected 'value' =>
object(Padd_Advertisement)[282]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-468x060.gif' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Banner Ad 1 (468x60)' (length=20)
protected 'description' => string 'The advertisement is placed beside the site name in the header.' (length=63)
1 =>
object(Padd_Input_Advertisement)[284]
protected 'keyword' => string 'magaling_ads_468060_2' (length=21)
protected 'value' =>
object(Padd_Advertisement)[283]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-468x060.gif' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Banner Ad 2 (468x60)' (length=20)
protected 'description' => string 'The advertisement is placed just below the title in Search Result page, Categories page, Tags page, Author page, and Archives page.' (length=131)
2 =>
object(Padd_Input_Advertisement)[279]
protected 'keyword' => string 'magaling_ads_125125_1' (length=21)
protected 'value' =>
object(Padd_Advertisement)[278]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-125x125.jpg' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Square Ad 1 (125x125)' (length=21)
protected 'description' => string 'The advertisement will be posted at the side bar.' (length=49)
3 =>
object(Padd_Input_Advertisement)[277]
protected 'keyword' => string 'magaling_ads_125125_2' (length=21)
protected 'value' =>
object(Padd_Advertisement)[276]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-125x125.jpg' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Square Ad 2 (125x125)' (length=21)
protected 'description' => string 'The advertisement will be posted at the side bar.' (length=49)
4 =>
object(Padd_Input_Advertisement)[275]
protected 'keyword' => string 'magaling_ads_125125_3' (length=21)
protected 'value' =>
object(Padd_Advertisement)[273]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-125x125.jpg' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Square Ad 3 (125x125)' (length=21)
protected 'description' => string 'The advertisement will be posted at the side bar.' (length=49)
5 =>
object(Padd_Input_Advertisement)[217]
protected 'keyword' => string 'magaling_ads_125125_4' (length=21)
protected 'value' =>
object(Padd_Advertisement)[216]
private 'img_url' => string 'http://localhost/TestSite/wp-content/themes/magaling/images/advertisement-125x125.jpg' (length=86)
private 'alt_desc' => string 'Padd Solutions' (length=14)
private 'web_url' => string 'http://www.paddsolutions.com' (length=28)
private 'target' => string '_new' (length=4)
private 'css_class' => string '' (length=0)
protected 'name' => string 'Square Ad 4 (125x125)' (length=21)
protected 'description' => string 'The advertisement will be posted at the side bar.' (length=49)
I don't know why the value in your script is lost, but there is something missing or wrong in the theme you are using or in the way you are calling the function.The supplied information is not enough, by the way.
Hope this helps.
回答6:
EDIT: Is unserialise call needed at all in:
$this->value = unserialize(get_option($keyword));
get_option returns unserialized object already (as long as it it was saved by update_option() - which automatically serialises object if necessary before saving it in database)
ORIGINAL ANSWER
I suspect the problem is with the way you serialize the object.
You need make sure the during serialization process (conversion to string representation) the type of the serialized object is KNOWN. Only then you can successfully unserialize (convert back from string representation to object) it back.
To answer your another question regarding _toString()
. This function is called magically by PHP when you are trying to use your object in a string context (when you try to echo the object for example). You DO NOT call this function directly.
Here is the single php file which demonstrates WORKING flow; with comments to the parts where you should look for traps (causes of problems). Save it as index.php and run to see the results.
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
class Padd_Input_Advertisement {
protected $keyword;
protected $value;
protected $name;
protected $description;
function __construct($keyword,$name,$description='') {
$this->keyword = $keyword;
$this->value = unserialize(get_option($keyword));
$this->name = $name;
$this->description = $description;
}
public function get_keyword() { return $this->keyword; }
public function set_keyword($keyword) { $this->keyword = $keyword; }
public function get_value() { return $this->value; }
public function set_value($value) { $this->value = $value; }
public function get_name() { return $this->name; }
public function set_name($name) { $this->name = $name; }
public function get_description() { return $this->description;}
public function set_description($description) { $this->description = $description; }
public function __toString() {
$strHTML = '';
$strHTML .= '<tr valign="top">';
$strHTML .= ' <th scope="row"><label for="' . $this->keyword . '">' . $this->name . '</label></th>';
$strHTML .= ' <td>';
$strHTML .= ' <label for="' . $this->keyword. '_alt_desc">Short Description</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_alt_desc" type="text" id="' . $this->keyword . '_alt_desc" value="' . $this->value->get_alt_desc() . '" size="80" /><br />';
$strHTML .= ' <label for="' . $this->keyword. '_img_url">Image URL</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_img_url" type="text" id="' . $this->keyword . '_img_url" value="' . $this->value->get_img_url() . '" size="80" /><br />';
$strHTML .= ' <label for="' . $this->keyword. '_web_url">Website</label><br />';
$strHTML .= ' <input name="' . $this->keyword . '_web_url" type="text" id="' . $this->keyword . '_web_url" value="' . $this->value->get_web_url() . '" size="80" />';
$strHTML .= ' <br /><small>' . $this->description . '</small>';
$strHTML .= ' </td>';
$strHTML .= '</tr>';
return $strHTML;
}
}
//Just a sample of the class representing your option object
class Class_Of_Value {
protected $alt_desc;
protected $img_url;
protected $web_url;
public function __construct($keyword) {
$this->alt_desc = 'description';
$this->img_url = 'image';
$this->web_url = 'web';
}
public function get_alt_desc() { return $this->alt_desc; }
public function get_img_url() { return $this->img_url; }
public function get_web_url() { return $this->web_url; }
}
//Sample of a CORRECT get_option function.
function get_option($keyword) {
//THIS IS IMPORTANT
$valueObject = new Class_Of_Value($keyword);
//MAKE SURE THAT TYPE of serialised value is KNOWN
return serialize($valueObject);
}
//Here is the root cause of your problem.
//Wordpress' get_option() DOES NOT serialise returned objects. It returns just value of an option pulled from database!
//Hence you can't call methods $this->value->get_alt_desc() etc.
define ('PADD_NAME_SPACE', ''); //dummy definition of the constant for the sake of PHP Notices
$padd_options['advertisements'] = array(
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_1',
'Square Ad 1 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_2',
'Square Ad 2 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_3',
'Square Ad 3 (125x125)',
'The advertisement will be posted at the side bar.'
),
new Padd_Input_Advertisement(
PADD_NAME_SPACE . '_ads_125125_4',
'Square Ad 4 (125x125)',
'The advertisement will be posted at the side bar.'
),
);
//Here __toString will be called by PHP outputing your HTML form filled with object values.
echo($padd_options['advertisements'][0]);
Suggested solution to your problem is to make sure that the Wordpress option is properly serialised (with object type) prior to being saved in database (WP’s update_option()
).
So, then get_option will return serialized string which will then be properly unserialised in your __construct
method.
回答7:
did you try to force the operator -> precedence?
I mean, from this:
$this->value->get_alt_desc()
to this:
($this->value)->get_alt_desc()
maybe your PHP interpreter is trying to evaluate value->get_alt_desc()
回答8:
So, I've figured out a solution to this problem that makes the code work as expected. It doesn't resolve the root cause, but it's more of a workaround.
In the __construct function I added:
$this->value2 = $this->value;
And then in the code that was causing the error I changed:
$this->value->get_alt_desc()
to
$this->value2->get_alt_desc()
So all of the code was working, except for that one variable $this->value was losing its value, and once I used a different variable name everything worked as it should have.
来源:https://stackoverflow.com/questions/13244321/this-value-losing-well-its-value