'Serialization of 'SimpleXMLElement' is not allowed when saving in Wordpress post_meta

user2078789 picture user2078789 · Feb 16, 2013 · Viewed 24.5k times · Source

I am working on an amazon affiliate wordpress page. For that I am using the aws_signed_request function to get the price and link from amazon.

Here is the aws_signed_request function returning the xml:

    function  aws_signed_request($region, $params, $public_key, $private_key, $associate_tag) {
    $method = "GET";
    $host = "ecs.amazonaws.".$region;
    $uri = "/onca/xml";

    $params["Service"]          = "AWSECommerceService";
    $params["AWSAccessKeyId"]   = $public_key;
    $params["AssociateTag"]     = $associate_tag;
    $params["Timestamp"]        = gmdate("Y-m-d\TH:i:s\Z");
    $params["Version"]          = "2009-03-31";

    ksort($params);

    $canonicalized_query = array();

    foreach ($params as $param=>$value)
    {
        $param = str_replace("%7E", "~", rawurlencode($param));
        $value = str_replace("%7E", "~", rawurlencode($value));
        $canonicalized_query[] = $param."=".$value;
    }

    $canonicalized_query = implode("&", $canonicalized_query);

    $string_to_sign = $method."\n".$host."\n".$uri."\n".
                            $canonicalized_query;

    /* calculate the signature using HMAC, SHA256 and base64-encoding */
    $signature = base64_encode(hash_hmac("sha256", 
                                  $string_to_sign, $private_key, True));

    /* encode the signature for the request */
    $signature = str_replace("%7E", "~", rawurlencode($signature));

    /* create request */
    $request = "http://".$host.$uri."?".$canonicalized_query."&Signature=".$signature;

    /* I prefer using CURL */
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,$request);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

    $xml_response = curl_exec($ch);

   if ($xml_response === False)
    {
        return False;
    }
    else
    {
        $parsed_xml = @simplexml_load_string($xml_response);
        return ($parsed_xml === False) ? False : $parsed_xml;
    }
} 

After that I get the asin from the post and generate the link and price

global $post;  
$asin = get_post_meta($post->ID, 'ASIN', true);

$public_key = 'xxxxxxxxxxx';
$private_key = 'xxxxxxxxxxx';
$associate_tag = 'xxxxxxxxxxx';

$xml = aws_signed_Request('de',
array(
  "MerchantId"=>"Amazon",
  "Operation"=>"ItemLookup",
  "ItemId"=>$asin,
  "ResponseGroup"=>"Medium, Offers"),
$public_key,$private_key,$associate_tag);

$item = $xml->Items->Item;

$link = $item->DetailPageURL;
$price_amount = $item->OfferSummary->LowestNewPrice->Amount;
if ($price_amount > 0) { 
    $price_rund = $price_amount/100;
    $price = number_format($price_rund, 2, ',', '.');
} else {
    $price= "n.v."; 
}

This all works pretty good when I echo the $link and $price. But I want to save the values in the custom fields of the wordpress post so I don't have to run the function every time.

update_post_meta($post->ID, 'Price', $price);
update_post_meta($post->ID, 'Link', $link);

This adds the price as the correct value, but when I want to add the link I get this error message:

Uncaught exception 'Exception' with message 'Serialization of 'SimpleXMLElement' is not allowed' in...

But when I remove the $parsed_xml=... function, it saves an empty value.

Answer

IMSoP picture IMSoP · Feb 16, 2013

(Nearly) everything returned when you are traversing a SimpleXML object is actually another SimpleXML object. This is what lets you write $item->OfferSummary->LowestNewPrice->Amount: requesting ->OfferSummary on the $item object returns an object representing the OfferSummary XML node, so you can request ->LowestNewPrice on that object, and so on. Note that this applies to attributes too - $someNode['someAttribute'] will be an object, not a string!

In order to get the string content of an element or attribute, you have to "cast" it, using the syntax (string)$variable. Sometimes, PHP will know you meant to do this, and do it for you - for instance when using echo - but in general, it's good practice to always cast to string manually so that you won't have any surprises if you change your code later. You can also cast to an integer using (int), or a float using (float).

The second part of your problem is that SimpleXML objects are stored rather specially in memory, and can't be "serialized" (i.e. turned into a string that completely describes the object). This means that if you try to save them into a database or session, you will get the error you're seeing. If you actually wanted to save a whole block of XML, you could use $foo->asXML().

So, in short:

  • use $link = (string)$item->DetailPageURL; to get a string rather than an object
  • use update_post_meta($post->ID, 'ItemXML', $item->asXML()); if you ever want to store the whole item