Yii2 and cXML

If you find yourself having to receive and respond to a 3rd party sending you cXML, you will want to set up a test environment on your localhost in preparation. cXML itself is not complicated except for the sheer number of child nodes, but it does stay true to some standards that make it not so hard to parse. If Coupa is the company you’re dealing with, you will most likely be contacted by a representative, not a tech person. They will show you samples of the cxml you will get, but have no idea how it is sent or received. I asked if the xml was a file or a string and I got stunned silence followed by yet another example of the formatting of the xml.

I managed to get things working in Yii2 myself after a lot of searching for clues. First, I set up a dummy php page outside of my yii application to simulate the 3rd party site sending the original cXML. For brevity, I am only going to show oversimplified samples of the cXML. There are plenty of samples of properly formatted cxml to be found.

Build up your xml as a string.




<?php

$xml = '<?xml version="1.0" encoding="UTF-8"?>';

$xml .= '<cXML payloadID="1xxxxx.xxxx@ip-xx.xx.xx.xx" timestamp="Mon May 19 18:29:48 +0000 2008" xml:lang="en-US">';

$xml .= '  <Header>';

$xml .= '    <From>';

$xml .= '      <Credential domain="DUNS">';

$xml .= '        <Identity>0000000</Identity>';

$xml .= '      </Credential>';

$xml .= '    </From>';

$xml .= '  </Header>';

//there would be more tags here...

$xml .= '</cXML>';



Define the url to your Yii2 action




$url = "http://localhost/yourapp/yourcontroller/youraction;



Then send the xml to your yii site with curl. I am totally in the dark about curl, but the following does work for me:




			$ch = curl_init($url);

			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

			curl_setopt($ch, CURLOPT_POST, 1);

			curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));

			curl_setopt($ch, CURLOPT_POSTFIELDS, "$xml");

			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);


			$data = curl_exec($ch); 

			echo $data;

			if(curl_errno($ch))

        print curl_error($ch);

      else

    curl_close($ch);



On the receiving side in your Yii2 app:

You have to disable csrf validation and use info from the cxml to confirm identity




public function beforeAction($action) 

{ 

    $this->enableCsrfValidation = false; 

    return parent::beforeAction($action); 

}


		

    public function behaviors()

    {

        return [

            'corsFilter' => [

                'class' => \yii\filters\Cors::className(),

            ],

        ];

		}	




public function actionCxml()

{

  if(\Yii::$app->request->isPost)

  {

    $cxml = file_get_contents("php://input"); //<<-- This is what stumped me USE THIS

    // $cxml = $_POST['somepostvariable']; //NO does not work

    $xml = simplexml_load_string($cxml);



Once you have $xml, you can parse away with simplexml, save the data to a model or whatever else you need. When the parsing is done you need to send back a cXML response.




$timestamp = date('Y-m-d\TH:i:sP');				

$response = '<?xml version="1.0" encoding="UTF-8"?>';

$response .= ' <cXML version="1.1.007" xml:lang="en-US" payloadID="'.$payloadID.' " timestamp="'.$timestamp.'">';

$response .= '  <Response>';

$response .= '  <Status code="200" text="OK"/>';

$response .= ' </Response>';

$response .= '</cXML>';

//$this->layout = 'cxml'; //Another mistake, don't use custom layout, use NO layout.

$xml_out = simplexml_load_string($response);			

\Yii::$app->response->format = 'xml'; //This is a custom response component see below 

return $response;

}


<?php

//Custom xml response component, see Yii2 cookbook for details https://github.com/samdark/yii2-cookbook

namespace app\components;


use yii\web\ResponseFormatterInterface;


class XmlArrayFormatter implements ResponseFormatterInterface

{

    public function format($response)

    {

        $response->getHeaders()->set('Content-Type', 'application/xml; charset=UTF-8');

        if ($response->data !== null) {

            $response->content = ($response->data);

        }

    }

}

?>