Showing posts with label xml. Show all posts
Showing posts with label xml. Show all posts

7.17.2007

To PHP5 or not to PHP5

I consider myself an Open Source Developer but I very rarely develop for the open source community. I create apps for businesses, where I work, and some side projects. I share code with those I work with obviously, but generally not with the PHP community as a whole. I share some items on this site that I think are helpful to people.

I'm not sure yet where I stand on the goPHP5 movement. I tend to use PHP5 on my newer development but still have to use PHP4 for legacy stuff. And by "legacy," I'm talking internet time here, so about 4 years is "legacy."

I'd like most of the common PHP apps to move to PHP5. I've got some PHP5 Wordpress plugins in my code repo right now. And heaven knows that Drupal could really use PHP5 (or even the namespace support in PHP6). But I think forcing their hand is a little harsh.

Photo Matt isn't moving Wordpress towards a PHP5 model any time soon. And this is a bummer. But I understand the point of not leaving users of your product in the dust. I don't really have that problem with my code.

As for my opinion, I feel like learning PHP5 was one of the best things I've done in my career. It helped the other object-oriented languages like Java and C# to seep into my head a little bit more. Knowing more then one language is always a good thing.

I love the object model in PHP5, as well as PDO, and the JSON extension is nice. But I find that I use the improved DOM handling of XML in PHP5 the most. It still really shocks me that more people aren't interested in using this. And it still shocks me that more people aren't interested in using XSLT and PHP.

8.18.2006

VS2005 Errors when creating .aspx pages that retrun XML or JSON

I kept getting this on a page that did nothing but reference a code behind.

Validation(): Element 'html' occurs too few times.

This is because VS2005 is performing HTML validation by default. Yuk. You can turn this off by going to:
  • Tools --> Options --> Text Editor --> HTML --> Validation
  • Uncheck the "Show Errors" checkbox

Voila!

8.16.2006

Handy AJAX data structures using JSON, XML, ADODB, PHP, and Mysql

I'm working on a project where I thought a little AJAX magic would help the user interface a bit, so I've been adding that functionality piece by piece. For this web site, we've previously standardized on using the ADODB PHP database abstraction library for all our queries.

The particular circumstances of the XMLHttpRequest callbacks usually required a few small rows of data to be returned. So, the challenge became how to use the ADODB record sets to create structures that were nice and portable for use with AJAX. Most of the examples of JSON code shows only the properties of one object being displayed. But to return multiple rows, you basically need an object with an object (ie, rows within a dataset)
There's currently quite a debate raging on whether to return XML or JSON for manipulating via JavaScript. I use both approaches depending on the situation but find that I prefer JSON the most. Here are functions for ADODB that will return both XML and JSON as well as examples of usage. These files can be copied from below or downloaded at:

http://www.boringguys.com/example_code/adodb/toxml.inc.txt
http://www.borignguys.com/example_code/adodb/tojson.inc.txt

Just remember to rename them to .php !!

Here's that same code:

toxml.inc.php
/** 
* Creates XML from the ADODB record set 
* 
* @param     object         $rs         - record set object 
* @param     bool        $moveFirst    - determines whether recordset is returned to first record 
* @return     string        $xml        - resulting xml 
* @version V1.0  10 June 2006  (c) 2006 Rich Zygler ( http://www.boringguys.com/ ). All rights reserved. 
* 
*    Released under both BSD license and Lesser GPL library license. You can choose which license 
*    you prefer. 
*/ 

function rs2xml($rs, $moveFirst = false) 
{ 
  if (!$rs) 
  { 
    printf(ADODB_BAD_RS,'rs2xml'); 
    return false; 
  } 

  $xml = ''; 
  $totalRows = 0; 
  $totalRows = $rs->numrows(); 

  $domxml = new DOMDocument('1.0', 'utf-8'); 
  $root = $domxml->appendChild($domxml->createElement('rows')); 
  $root->setAttribute('total-rows', $totalRows); 

  $row_count = 1; 
  while($line = $rs->fetchRow()) 
  { 
    $row = $root->appendChild($domxml->createElement('row')); 

    foreach ($line as $col_key => $col_val) 
    { 
      $col = $row->appendChild($domxml->createElement('column')); 
      $col->setAttribute('name', strtolower($col_key)); 
      $col->appendChild($domxml->createTextNode($col_val)); 
    } 
    $row_count++; 
  } 
  $domxml->formatOutput = true; 
  $xml = $domxml->saveXML(); 
  $domxml = null; 

  if ($moveFirst) 
  { 
    $rs->MoveFirst(); 
  } 
  return $xml; 
} 


tojson.inc.php
/** 
* Creates JSON ( http://www.json.org/ ) from the ADODB record set 
* 
* @param     object         $rs         - record set object 
* @param     bool        $moveFirst    - determines whether recordset is returned to first record 
* @return     string        $output        - resulting json string 
* @version V1.0  10 June 2006  (c) 2006 Rich Zygler ( http://www.boringguys.com/ ). All rights reserved. 
* 
*    Released under both BSD license and Lesser GPL library license. You can choose which license 
*    you prefer. 

Example output from query  "SELECT Name, Continent From Country LIMIT 10;" 

{"rows":[ 
{"row":{"Name":"Afghanistan","Continent":"Asia"}}, 
{"row":{"Name":"Netherlands","Continent":"Europe"}}, 
{"row":{"Name":"Netherlands Antilles","Continent":"North America"}}, 
{"row":{"Name":"Albania","Continent":"Europe"}}, 
{"row":{"Name":"Algeria","Continent":"Africa"}}, 
{"row":{"Name":"American Samoa","Continent":"Oceania"}}, 
{"row":{"Name":"Andorra","Continent":"Europe"}}, 
{"row":{"Name":"Angola","Continent":"Africa"}}, 
{"row":{"Name":"Anguilla","Continent":"North America"}}, 
{"row":{"Name":"Antigua and Barbuda","Continent":"North America"}} 
]} 

*/ 

function rs2json($rs, $moveFirst = false) 
{ 
  if (!$rs) 
  { 
    printf(ADODB_BAD_RS,'rs2json'); 
    return false; 
  } 

  $output = ''; 
  $rowOutput = ''; 

  $output .= '{"rows":'; 
  $totalRows = $rs->numrows(); 

  if($totalRows > 0) 
  { 
    $output .= '['; 
    $rowCounter = 1; 
    while ($row = $rs->fetchRow()) 
    { 
      $rowOutput .= '{"row":{'; 
      $cols = count($row); 
      $colCounter = 1; 
      
      foreach ($row as $key => $val) 
      { 
        $rowOutput .= '"' . $key . '":'; 
        $rowOutput .= '"' . $val . '"'; 

        if ($colCounter != $cols) 
        { 
          $rowOutput .= ','; 
        } 
        $colCounter++; 
      } 

      $rowOutput .= '}}'; 

      if ($rowCounter != $totalRows) 
      { 
        $rowOutput .= ','; 
      } 
      $rowCounter++; 
    } 
    $output .= $rowOutput . ']'; 
  } 
  else 
  { 
    $output .= '"row"'; 
  } 

  $output .= '}'; 

  if ($moveFirst) 
  { 
    $rs->MoveFirst(); 
  } 
  return $output; 
} 


Example Usage

Place both of the files into your existing ADODB directory. If you don't already have the "world" sample database from mysql set up, please go and get it and install it ( http://dev.mysql.com/doc/ ). Make sure that the toxml and tojson files from above are placed in your adodb/ dir. Then use the following sample pages. Make sure to change your db settings in the file to what's appropriate. You will need PHP5 for the XML example. But you could easily mock up a version of that in PHP4, or you could always use this.

To test it out just do something like this for xml


or this for json

7.25.2006

XForms cometh to PHP ! Throw out HTML_Quickform?

PHP Architect has an article in their June 2006 issue about using XForms within PHP5. If you don’t know what XForms is/are, you’d better look into it. It’s basically a way to describe forms on your page and all the data validation rules entirely in XML. The form presentation and validation capabilities of it are kind of like HTML_Quickform on crack. Of course, there’s always a rub. No browsers natively support XForms. The article in PHPArch uses the FormFaces JavaScript to get things to work. But I don’t like the way FormFaces comes off. They tout both a GPL license and a commercial one but then they mask the JavaScript so you can’t extend it or see how it’s actually working. That smells like a temporary GPL license to me. So, I don’t think you could do XForms on the client side just yet. It’d be nice if someone would step up and create a server-based solution for XForms in PHP. Maybe I’ll do that myself. In the meantime though, the Quickform folks are already busy planning HTML_Quickform2, a PHP5 version of their form production code. That will have to do for now I suppose.

7.22.2006

Smarty not so smarty in PHP5. Try XSLT instead.

So I’ve been working on this pretty big migration project lately, from PHP4 / MySQL 4 to PHP5 / MySQL5. It’s even larger because we’re moving from a largely procedural code base to using objects with inheritance and a lot of composition.

So what’s all that have to do with Smarty? Well, all our PHP devs are busy getting familiar with the framework I’ve built for the project. So, that leaves some HTML gurus and ASP folks sitting around under-utilized on this project. Ideally, I’d like the PHP folks to work on the backend stuff and then have an HTML or ASP dev take care of the views.

Of course, if the templates are all done in Smarty that means these folks have to possess not only a decent understanding of PHP, but also thorough experience with Smarty templating. These skills are a bit of a reach for the HTML folks… and the ASP people will mostly just protest because they’d have to learn PHP stuff AND develop on a Linux box. ;-)

Enter XSLT. Most developers in other languages use XSLT already (ASP, .NET, JSP, etc). I’ve never understood why it’s so slow to catch on with PHP. Perhaps because the XML tools in PHP4 were so poor?

So if the ASP folks already know XSLT, and the HTML folks don’t yet, but can learn a templating skill that is basically development-language-agnostic, I think it’s a win-win-win situation.

I’ve thought this way for a long time but this project has really crystallized my take on it. Developers at any level should strive to learn tools and techniques that can be universally applied to other languages. You may be a PHP programmer today, but you might be something else tomorrow.

11.09.2005

The SqlXML class -- PHP sql query results in XML via object

For a personal project that I’m working on in PHP4, I’m using XSLT to display a bunch of pages. Most times, I don’t use the XSL transforms for the whole page, usually just tables of data that are returned from a query. The first thing that you need to do an XSL transform is a nice chunk of XML data. It would be pretty handy if when we queried the database, it returned our results as XML that we could use for the transform. More robust database systems like Oracle can do this right out of the box, but Mysql needs some help from PHP. I created a nice little object to take care of this for me.

The object does a few things. There are two methods in the object, query and queryPaged. The query method just does a straight query to the db and returns the results as XML. The queryPaged method also does a query to the db and returns the results as XML. But this method takes on a few extra parameters so that it can return page navigation information in the XML also. This way, you can set up paged data tables with navigation (page 1, page 2, etc.) instead of just glomming out hundreds of rows of data.

First things first – The query method


First off, we need to include our db connection stuff and our SqlXML object file. Then we can create our sql. Then instantiate the SqlXML object and pass it our sql. Here's that code:

// start db connection 
require_once('db.php'); // left to the reader to do 
require_once('SqlXML.php'); // full code to this program below 

$sql = "SELECT * FROM customers LIMIT 10"; 

$sqlxml =& new SqlXML(); 
$xml = $sqlxml->query($sql); 

Pretty simple right? If you want to see the results, you can always just echo out a textarea:

echo '';

Your results will obviously depend on your query, but an excerpt of mine look something like this:

<?xml version="1.0"?> 

  
    
      1
      Harry
      harry@test.com 
    
    
      2
      Tom
      tom@test.com 
    
 


So the method creates an XML “row” element for each row returned. Each field in the row returned gets its own “column” element. Each “column” element has a “name” attribute which tells you which field that data is for.

Keep in mind here that $xml is a string and not an xml object at this point. All the domxml stuff is handled inside the SqlXML object so we don’t have to fuss with it outside the object.

So how does this thing work? Well, first, it executes the query. If something didn’t work in the query, it just returns this as a string, not as XML.

function query($query) 
{ 
  $xml = ''; 
  $result = mysql_query($query); 
  if (!$result) 
  { 
    return 'Query failed: ' . mysql_error(); 
  }

Then it records the number of rows in the data.

$totalRows = mysql_num_rows($result);

The method then starts to build the xml tree by setting a result-set element at the root with an attribute of total-rows. The method then goes on to build the rest of the xml tree, row by row. I should note here that you do need the domxml extensions for PHP installed. All the XML creation in the object is done with domxml methods, no lame XML-as-string solutions here. Here’s the code snip of that area of the code:

$rowCount = 1; 
while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
{ 
  $row = $root->append_child($dom->create_element("row")); 
  $row->set_attribute("number", $rowCount); 

  foreach ($line as $colKey => $colValue) 
  { 
    $col = $row->append_child($dom->create_element("column")); 
    $col->set_attribute("name", strtoupper($colKey)); 
    $col->append_child($dom->create_text_node($colValue)); 
  } 
  $rowCount++; 
} 

Here, the while loop creates the row elements in our resulting xml and the foreach loop creates the column elements.

Once you have your xml in that $xml variable, you can bring it in to your XSL transform (article coming soon...). Here is the full code for using the query method of the object.

// start db connection 
require_once('db.php'); // left to the reader to do 
require_once('SqlXML.php'); // full code to this program below 

$sql = "SELECT * FROM customers LIMIT 10"; 

$sqlxml =& new SqlXML(); 
$xml = $sqlxml->query($sql); 
echo ''; 

// then take that $xml string and transform with the appropriate XSL 

Now what if you wind up with a lot of data from your query? For me, if I'm displaying any more than about 20 rows of data, I'm going to paginate it (place 20 rows on each page with some page to page naviation). We can use the queryPaged method of the SqlXML object for this. But I'll save that for another article.

Here's the complete SqlXML.php class.

class SqlXML 
{ 
  var $resultsPerPage = 0; 
  var $curPage = 0; 
  var $sort_by; 
  var $sort_dir; 

  function SqlXML() 
  { 

  } 

  function query($query) 
  { 
    $xml = ''; 
    $result = mysql_query($query); 
    if (!$result) 
    { 
      return 'Query failed: ' . mysql_error(); 
    }
 
    $totalRows = mysql_num_rows($result); 
    $dom = domxml_new_doc("1.0"); 
    
    $root = $dom->append_child($dom->create_element('result-set')); 
    $root->set_attribute('total-rows',$totalRows); 

    $rowCount = 1; 
    while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
    { 
      $row = $root->append_child($dom->create_element("row")); 
      $row->set_attribute("number", $rowCount); 

      foreach ($line as $colKey => $colValue) 
      { 
        $col = $row->append_child($dom->create_element("column")); 
        $col->set_attribute("name", strtoupper($colKey)); 
        $col->append_child($dom->create_text_node($colValue)); 
      } 
      $rowCount++; 
    } 

    $xml = $dom->dump_mem(); 

    // Free resultset 
    mysql_free_result($result); 
    return $xml; 
  } 

  function queryPaged($queryFields, $queryTail, $scriptPath = '') 
  { 
    $recCount = 0; 
    $totalPages = 0; 
    $xml = ''; 

    // get the total count without getting field data 
    $queryCt = "SELECT COUNT(*) " . $queryTail; 
    $resultCt = mysql_query($queryCt); 

    if (!$resultCt) 
    { 
      return 'Query failed: ' . mysql_error(); 
    } 

    // calc total pages 
    $row = mysql_fetch_row($resultCt); 
    $recCount = $row[0]; 

    if ($recCount > 0) 
    { 
      $query = "SELECT " . $queryFields . $queryTail; 
      if ($this->resultsPerPage != 0) 
      { 
        $recStart = ($this->curPage * $this->resultsPerPage) - $this->resultsPerPage; 

        // add limit to query 
        $query .= " LIMIT $recStart, $this->resultsPerPage"; 
      } 

      mysql_free_result($resultCt); 
      $result = mysql_query($query); 

      if (!$result) 
      { 
        return 'Query failed: ' . mysql_error(); 
      } 

      // calc total pages 
      $recCount2 = mysql_num_rows($result); 
      if (!$recCount2) 
      { 
        return 'Query failed : ' . mysql_error(); 
      } 

      $totalPages = floor($recCount / $this->resultsPerPage); 
      if ($recCount % $this->resultsPerPage > 0 ) 
      { 
        $totalPages++; 
      } 
    } 

    // create xml 
    $dom = domxml_new_doc("1.0"); 
    $rs = $dom->append_child($dom->create_element('result-set')); 
    $rs->set_attribute('total-rows',$recCount); 
    $rs->set_attribute('results-per-page', $this->resultsPerPage); 
    $rs->set_attribute('total-pages', $totalPages); 
    $rs->set_attribute('current-page', $this->curPage); 
    $rs->set_attribute('script-path', $scriptPath); 

    if (isset($this->sort_by) && isset($this->sort_dir) && $this->sort_by != '' && $this->sort_dir != '') 
    { 
      $rs->set_attribute('sort-by', $this->sort_by); 
      $rs->set_attribute('sort-dir', $this->sort_dir); 
    } 

    $nav = $rs->append_child($dom->create_element('nav')); 

    // create XML to set up paging 
    for ($i = 1; $i < = $totalPages; $i++) 
    { 
      $page = $nav->append_child($dom->create_element('page')); 
      $page->set_attribute('num', $i); 
      if ($i == $this->curPage) 
      { 
        $page->set_attribute('current-page', 'true'); 
      } 
    } 

    $rows = $rs->append_child($dom->create_element('rows')); 

    if ($recCount > 0) 
    { 
      $rowCount = 1; 
      while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
      { 
        $row = $rows->append_child($dom->create_element("row")); 
        $row->set_attribute("number", $rowCount); 

        foreach ($line as $colKey => $colValue) 
        { 
          $col = $row->append_child($dom->create_element("column")); 
          $col->set_attribute("name", strtoupper($colKey)); 
          $col->append_child($dom->create_text_node($colValue)); 
        } 
        $rowCount++; 
      } 
    } 

    if (is_resource($result)) 
    { 
      mysql_free_result($result); 
    } 
    $xml = $dom->dump_mem(); 

    return $xml; 
  } 
}