debuggable

 
Contact Us
 
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50

Composing Methods: Inline Temp

Posted on 11/6/07 by Tim Koschützki

You have a temporary variable that is created with a simple expression. Now the temp is getting into the way of other refactorings. Replace all references to the temporary variable with the expression.

Motivation

Often you will find yourself having temporary variables that carry the contents of simple method calls. A good thing is to remove the temporary variables and replace all references with the method or function invoked. This leads to much clearer code, as the method or function name ideally speaks for itself already, and saves you one code line. The less code the better!

Also, often you can use Inline Temp when you are having too many temporary variables around so that extracting functionality into their own methods (called Extract Method) becomes difficult. An article about Extract Method will be written later.

Beware of using Inline Temp in connection with loops as this can sometimes cause disastrious performance issues. Check Optimising Loops for further information about that.

Code Example

Before

function underHeavyLoad($database) {
   $numVisitors = $database->getNumberCurrentOnlineUsers();
   return ($numVisitors > 200);
}

After

function underHeavyLoad($database) {
    return ($database->getNumberCurrentOnlineUsers() > 200);
}

 

How To Generate Monochromatic CSS Stylesheets Within Seconds Using PHP

Posted on 7/6/07 by Tim Koschützki

Hi folks! As a follow-up to my previous article Control your CSS via PHP - Good Stuff I have tried to implement the techniques explained there to build monochromatic stylesheets within seconds. First off, I will explain to the ones, that do not know yet, what a monochromatic color scheme is.

What Is A Monochromatic Color Scheme?

The monochromatic color scheme uses variations in lightness and saturation of a single color. It looks clean and elegant. Monochromatic colors go well together, producing a soothing effect. The monochromatic scheme is very easy on the eyes, especially with blue or green hues.
You can use it to establish an overall mood, which we will do later with the css generation. The primary color can be integrated with neutral colors such as black, white, or gray. However, it can be difficult, when using this scheme, to highlight the most important elements.

The monochromatic color scheme is perfect for our task of generating CSS via PHP based on forming shades of a base color. First off, please download the PHP Script for generating CSS. Place it in any folder on your website or your local php installation.

Setting Up Our CSS Generation Environment

Next off we will borrow some XHTML / CSS code from my buddy Paul O'Brian, one of the greatest CSS Gurus on the net. His site features plenty of css goodness, tutorials and effects I am sure you haven't seen before. It is definately worth to check out! We will take the code of his universal three column layout that works in most browsers and allows any column to be the longest. Save that markup to a file named "test.html" in the same folder where you saved the php file. Now, in that same folder, create a file "css.php" where you insert the following CSS code:


// Tell the browser that this is CSS instead of HTML
header("Content-type: text/css");

// Get the color generating code
require_once("csscolor.php");

// Define a couple color palettes
//$base = new CSS_Color('C9E3A6');
$base = new CSS_Color('99E456');

// Trigger an error just to see what happens
// $trigger = new CSS_Color('');
/* CSS Document */
* {margin:0;padding:0}
p {margin-bottom:1em}
ul{margin-left:20px;margin-bottom:1em}
/* commented backslash hack v2 \*/
html, body{height:100%;}
/* end hack */

body {
  background:#<?php echo $base->bg['-2'] ?> repeat-y left top;
  color: #FFF;
}
#outer{
  margin-left:130px;
  margin-right:130px;
  background: #<?php echo $base->bg['0'] ?>;
  border-left:1px solid #000;
  border-right:1px solid #000;
  margin-bottom:-52px;
  min-height:100%
}
#header{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:70px;
  background: #<?php echo $base->bg['-3'] ?>;
  border-top:1px solid #000;
  border-bottom:1px solid #000;
  overflow:hidden;
}
#left {
  position:relative;/*ie needs this to show float */
  width:130px;
  float:left;
  margin-left:-129px;/*must be 1px less than width otherwise won't push footer down */
  z-index:100;
  left:-1px;
}
#left p,
#right p {padding:3px}
#right {
  position:relative;/*ie needs this to show float */
  width:130px;
  float:right;
  margin-right:-129px;/*must be 1px less than width otherwise won't push footer down */
  left:1px;
  color: #000;
}
#footer {
  width:100%;
  clear:both;
  height:50px;
  border-top:1px solid #000;
  border-bottom:1px solid #000;
  background-color: #<?php echo $base->bg['-4'] ?>;
  text-align:center;
  position:relative;
}
#clearheader{height:72px;}/*needed to make room for header*/
#clearfooter{clear:both;height:52px;}/*needed to make room for footer*/
* > html #clearfooter {float:left;width:100%;}/* ie mac styles */
#centrecontent {
  width:100%;
  float:left;
  position:relative;
  z-index:1;
  margin:0 -1px;/* moz fix*/
  color: #000;
}
/* css stuff below is just for presentation and not needed for the demo */
#centrecontent p { padding: 0 20px; }
h1 {font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: lighter; font-size: 150%; padding: 20px;}
#left span {
  display:none
} 
#left a:hover {
  text-decoration: none;
  text-decoration: none;
  color:#222;
  background: #FFF;
}
#left a:hover span {
  display:block;
  position:absolute;
  left:130px;
  width:150px;
  z-index:20;
  background:#fff;
}
@media all and (min-width: 0px){
  #left a:hover span {
  top:150px;
  }
}
#footer a, #left a { color:#FFF; }
#footer span {
  display:none
} 
#footer a:hover {
  text-decoration: none;
  color:#222;
  background: #FFF;
}
#footer a:hover span {
  display:block;
  position:absolute;
  top:-95px;
  width:150px;
  z-index:20;
  background:#fff;
  left:50%;
}
html>body #minHeight{float:right;width:0px;height:100%;margin-bottom:-52px;} /*safari wrapper */

What this does is simply using our css generation script to alter the code of the original css file from the universal three column layout. We create a base color ($base = new CSS_Color('99E456');) and form shade variations for the two navbars, the header and the footer:

body {
  background:#<?php echo $base->bg['-2'] ?> repeat-y left top;
  color: #FFF;
}
#outer{
  margin-left:130px;
  margin-right:130px;
  background: #<?php echo $base->bg['0'] ?>;
  border-left:1px solid #000;
  border-right:1px solid #000;
  margin-bottom:-52px;
  min-height:100%
}
#header{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:70px;
  background: #<?php echo $base->bg['-3'] ?>;
  border-top:1px solid #000;
  border-bottom:1px solid #000;
  overflow:hidden;
}
#footer {
  width:100%;
  clear:both;
  height:50px;
  border-top:1px solid #000;
  border-bottom:1px solid #000;
  background-color: #<?php echo $base->bg['-4'] ?>;
  text-align:center;
  position:relative;
}

Now change the line

in your site.html file and then launch it via a browser. You should see something like this:

Picture of first generated layout

Changing The Base Color For Some Cool Results

It is not exactly pretty, but we can play around with the base color a bit:

$base = new CSS_Color('98ccac');
Picture of second generated layout

$base = new CSS_Color('ccac99');
Picture of thirdgenerated layout

$base = new CSS_Color('ccccff');
Picture of forth generated layout

What Other Uses For This Can We Think Of?

Now you could also add other colors to highlight some other parts, like singleing out the footer from the monochromatic scheme or always staying with white background for the main content column.

$highlightFooter = new CSS_Color('973432');
$outer = new CSS_Color('ffffff');
...

We could also generate a grayscale website using white or black as the base color. I am sure there are endless cool possibilities for this stuff, like different shades for list elements, headings, paragraphs and what not. I am eager to hear your ideas in the post comments below!

So How Does This PHP Thing Work?

When you look at the csscolor.php file, you will quickly notice that it is a very well-made object-oriented PHP script. The foundation of the magic is here:

function CSS_Color($bgHex, $fgHex='')
  {
    // This is the constructor method for the class,
    // which is called when a new object is created.

    // Initialize this PEAR object so I can
    // use the PEAR error return mechanism
    $this->PEAR();

    // Initialize the palette
    $this->setPalette($bgHex, $fgHex);
  }

  //==================================================
  //==METHODS=========================================
  //==================================================

  //--------------------------------------------------
  function setPalette($bgHex, $fgHex = '')
  {
    // Initialize the color palettes

    // If a foreground color was not specified,
    // just use the background color.
    if (!$fgHex) {
      $fgHex = $bgHex;
    }

    // Clear the existing palette
    $this->bg = array();
    $this->fg = array();

    // Make sure we got a valid hex value
    if (!$this->isHex($bgHex)) {
      $this->raiseError("background color '$bgHex' is not a hex color value",
      __FUNCTION__, __LINE__);
      return false;
    }

    // Set the bg color
    $this->bg[0] = $bgHex;

    $this->bg['+1'] = $this->lighten($bgHex, .85);
    $this->bg['+2'] = $this->lighten($bgHex, .75);
    $this->bg['+3'] = $this->lighten($bgHex, .5);
    $this->bg['+4'] = $this->lighten($bgHex, .25);
    $this->bg['+5'] = $this->lighten($bgHex, .1);

    $this->bg['-1'] = $this->darken($bgHex, .85);
    $this->bg['-2'] = $this->darken($bgHex, .75);
    $this->bg['-3'] = $this->darken($bgHex, .5);
    $this->bg['-4'] = $this->darken($bgHex, .25);
    $this->bg['-5'] = $this->darken($bgHex, .1);
..

As you see, the bg-property's values are set via the CssColor::lighten() and CssColor::darken() methods. So, what are we waiting for? Let's look at them:

function lighten($hex, $percent)
  {
    return $this->mix($hex, $percent, 255);
  }

  //--------------------------------------------------
  function darken($hex, $percent)
  {
    return $this->mix($hex, $percent, 0);
  }

Hrm okay, nothing really here, escept that we now know the second parameter is treated as a percentage. This makes me think if it is possible to add more shades to choose from. We will look at that later. Let's look at the mix() method. I will leave out the error handling parts to preserve some space. :)

function mix($hex, $percent, $mask)
  {
    // ...

    for ($i=0; $i&#lt;3; $i++) {
      $rgb[$i] = round($rgb[$i] * $percent) + round($mask * (1-$percent));

      // In case rounding up causes us to go to 256
      if ($rgb[$i] > 255) {
  $rgb[$i] = 255;
      }

    }
    return $this->RGB2Hex($rgb);
  }

  //--------------------------------------------------
  function hex2RGB($hex)
  {
     // ...

    // Regexp for a valid hex digit
    $d = '[a-fA-F0-9]';
   
    // Make sure $hex is valid
    if (preg_match("/^($d$d)($d$d)($d$d)\$/", $hex, $rgb)) {
     
      return array(
       hexdec($rgb[1]),
       hexdec($rgb[2]),
       hexdec($rgb[3])
       );
    }
    if (preg_match("/^($d)($d)($d)$/", $hex, $rgb)) {
     
      return array(
       hexdec($rgb[1] . $rgb[1]),
       hexdec($rgb[2] . $rgb[2]),
       hexdec($rgb[3] . $rgb[3])
       );
    }

    $this->raiseError("cannot convert hex '$hex' to RGB", __FUNCTION__, __LINE__);
    return false;
  }

  //--------------------------------------------------
  function RGB2Hex($rgb)
  {
    // ...

    $hex = "";
    for($i=0; $i < 3; $i++) {

      // Convert the decimal digit to hex
      $hexDigit = dechex($rgb[$i]);

      // Add a leading zero if necessary
      if(strlen($hexDigit) == 1) {
  $hexDigit = "0" . $hexDigit;
      }

      // Append to the hex string
      $hex .= $hexDigit;
    }

    // Return the complete hex string
    return $hex;
  }

Okay, here is the magic! We simply calculate an array of rgb values out of the supplied hex color string, alternate its contents via some cool math formula ($rgb[$i] = round($rgb[$i] * $percent) + round($mask * (1-$percent));) and convert it back to hex and return it! This is actually quite simple.

Let us implement more shades. In the CssColor::setPalette() change

$this->bg['+1'] = $this->lighten($bgHex, .85);
    $this->bg['+2'] = $this->lighten($bgHex, .75);
    $this->bg['+3'] = $this->lighten($bgHex, .5);
    $this->bg['+4'] = $this->lighten($bgHex, .25);
    $this->bg['+5'] = $this->lighten($bgHex, .1);

    $this->bg['-1'] = $this->darken($bgHex, .85);
    $this->bg['-2'] = $this->darken($bgHex, .75);
    $this->bg['-3'] = $this->darken($bgHex, .5);
    $this->bg['-4'] = $this->darken($bgHex, .25);
    $this->bg['-5'] = $this->darken($bgHex, .1);

to

$this->bg['+1'] = $this->lighten($bgHex, .95);
    $this->bg['+2'] = $this->lighten($bgHex, .9);
    $this->bg['+3'] = $this->lighten($bgHex, .85);
    $this->bg['+4'] = $this->lighten($bgHex, .8);
    $this->bg['+5'] = $this->lighten($bgHex, .75);
    $this->bg['+6'] = $this->lighten($bgHex, .7);
    $this->bg['+7'] = $this->lighten($bgHex, .65);
    $this->bg['+8'] = $this->lighten($bgHex, .6);
    $this->bg['+9'] = $this->lighten($bgHex, .55);
    $this->bg['+10'] = $this->lighten($bgHex, .5);
    $this->bg['+11'] = $this->lighten($bgHex, .45);
    $this->bg['+12'] = $this->lighten($bgHex, .4);
    $this->bg['+13'] = $this->lighten($bgHex, .35);
    $this->bg['+14'] = $this->lighten($bgHex, .3);
    $this->bg['+15'] = $this->lighten($bgHex, .25);
    $this->bg['+16'] = $this->lighten($bgHex, .2);
    $this->bg['+17'] = $this->lighten($bgHex, .15);    
    $this->bg['+18'] = $this->lighten($bgHex, .1);
    $this->bg['+19'] = $this->lighten($bgHex, .05);  
   
    $this->bg['-1'] = $this->darken($bgHex, .95);
    $this->bg['-2'] = $this->darken($bgHex, .9);
    $this->bg['-3'] = $this->darken($bgHex, .85);
    $this->bg['-4'] = $this->darken($bgHex, .8);
    $this->bg['-5'] = $this->darken($bgHex, .75);
    $this->bg['-6'] = $this->darken($bgHex, .7);
    $this->bg['-7'] = $this->darken($bgHex, .65);
    $this->bg['-8'] = $this->darken($bgHex, .6);
    $this->bg['-9'] = $this->darken($bgHex, .55);
    $this->bg['-10'] = $this->darken($bgHex, .5);
    $this->bg['-11'] = $this->darken($bgHex, .45);
    $this->bg['-12'] = $this->darken($bgHex, .40);
    $this->bg['-13'] = $this->darken($bgHex, .35);
    $this->bg['-14'] = $this->darken($bgHex, .3);
    $this->bg['-15'] = $this->darken($bgHex, .25);
    $this->bg['-16'] = $this->darken($bgHex, .2);
    $this->bg['-17'] = $this->darken($bgHex, .15);
    $this->bg['-18'] = $this->darken($bgHex, .1);
    $this->bg['-19'] = $this->darken($bgHex, .05);

This gives us a whole range of new shades! Here are some stylesheets I could come up with using them:

Picture of fifth generated layout

Picture of forth generated layout

Picture of seventh generated layout

Much credit goes to Patrick Fitzgerald for this very nice script!

Thanks for reading! Have a good one, all!

 

Parsing XML With The DOM Library

Posted on 5/6/07 by Tim Koschützki

The PHP 4 DOMXML extension has undergone some serious transformation since PHP5 and is a lot easier to use. Unlike SimpleXML, DOM can, at times, be cumbersome and unwiedly. However, it is often a better choice than SimpleXML. Please join me and find out why.

Since SimpleXML and DOM objects are interoperable you can use the former for simplicity and the latter for power. How you can exchange data between the two extensions is explained at the bottom of the article.
The DOM extension is especially useful when you want to modify XML documents , as SimpleXML for example does not allow to remove nodes from an XML document. For this article's code examples we will use the same foundation that we used in the Parsing XML with SimpleXML post.
We will use this very site's google sitemap file, which can be downloaded here. The sitemap.xml file features an xml list of pages of php-coding-practices.com for easy indexing in google.

Loading and Saving XMLDocuments

The DOM extension, just like SimpleXML, provides two ways to load xml documents - either by string or by filename:

$source = 'sitemap.xml';

$dom = new DomDocument();
$dom->load($source);

// load as string
$dom2 = new DomDocument();
$dom2->loadXML(file_get_contents($source));

In addition to that, the DomDocument object provides two functions to load html files. The advantage is that html files do not have to be well-formed to load. Here is an example:

$doc = new DOMDocument();
$doc->loadHTML("<html><body>Test
</body></html>"
);
echo $doc->saveHTML();

The cool news is that mal-formed HTML will automatically be transferred into well-formed one. Look at this script:

$doc = new DOMDocument();
$doc->loadHTML("<html><body><p>Test
</p></body></html>"
);
echo $doc->saveHTML();

The DomDocument::loadHTML() method will automatically add a DTD (Document Type Definition) and add the missing end-tag for the opened p-tag. Cool, isn't it?

< !DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Test
</p></body></html>

Saving XML data with the DOM library is as easy. Just use DomDocument::saveHTML() and DomDocument::saveXML() with no parameters. They will automatically create XML or HTML documents from your xml contents and return them. DomDocument::saveHTMLFile() and DomDocument::save() save to html and xml files. They request a filepath paramter as a string.

XPath Queries

One of the most powerful features of the DOM extension is the way in which it integrates with XPath queries. In fact, DomXpath is much more powerful than its SimpleXML equivalent:

$source = 'sitemap.xml';
$dom = new DomDocument();
$dom->load($source);

$xpath = new DomXPath($dom);
$xpath->registerNamespace('c', 'http://www.google.com/schemas/sitemap/0.84');
$result = $xpath->query("//c:loc/text()");
echo $result->length.'
'
;
//echo $result->item(3)->data;
foreach($result as $b) {
  echo $b->data.'
'
;
}

Notice that the sitemap xml file contains a namespace already, which we register using DomXPath::registerNamespace():

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

We really have to register that namespace with the DomXPath object or else it will not know where to search. ;) You can also register multiple namespaces, but more on that later. Notice that we use text() within the xpath query to get the actual text contents of the nodes.
If you want to learn the ins and outs of the xpath language, I recommend reading the W3C XPath Reference.

Modifying XML Documents

Adding New Nodes

To add new data to a loaded dom documented, we need to create new DomElement objects by using the DomDocument::createElement(), DomDocument::createElementNS() and DomDocument::createTextNode() methods.
In the following we will add a new url to our urlset:

$source = 'sitemap.xml';
$dom = new DomDocument();
$dom->load($source);

// url element
$url = $dom->createElement('url');


// location
$loc = $dom->createElement('loc');
$text = $dom->createTextNode('http://php-coding-practices.com/article/');
$loc->appendChild($text);

// last modification
$lastmod= $dom->createElement('lastmod');
$text = $dom->createTextNode('2007-04-20T10:24:32+00:00');
$lastmod->appendChild($text);

// change frequency
$changefreq= $dom->createElement('changefreq');
$text = $dom->createTextNode('weekly');
$changefreq->appendChild($text);

// priority
$priority= $dom->createElement('priority');
$text = $dom->createTextNode('0.3');
$priority->appendChild($text);
   
// add the elements to the url   
$url->appendChild($loc);
$url->appendChild($lastmod);
$url->appendChild($changefreq);
$url->appendChild($priority);

// add the new url to the root element (urlset)
$dom->documentElement->appendChild($url);

echo $dom->saveHtml();

The code is pretty self-explanatory. First we create a new url element as well as some sub-elements. Then we append those sub-elements to the url element, which we in turn append to the document's root element. Note that the root element can be accessed via the $dom->documentElement property. The output:

....
  <loc>http://php-coding-practices.com/2007/04/</loc>

    <lastmod>2007-04-30T16:54:58+00:00</lastmod>
    <changefreq>yearly</changefreq>
    <priority>0.5</priority>

 
  <url>
    <loc>http://php-coding-practices.com/2007/03/</loc>
    <lastmod>2007-03-29T20:04:51+00:00</lastmod>

    <changefreq>yearly</changefreq>
    <priority>0.5</priority>

  </url>

Now it was certainly not as easy as it would have been had we used SimpleXML. The DOM extension provides many more methods for more power. For example you can associate a namespace with an element
while creation using DomDocument::createElementNS(). I will provide some example code on that later in the article.

Adding Attributes To Nodes

Via DomDocument::setAttribute() we can easily add an attribute to a node object. Example:

$url = $dom->createElement('url');
...
$url->setAttribute('meta:level','3');

Here we set a fictive meta:level attribute with the value 3 to our url NodeElement from above.

Moving Data

Moving data is not as obvious as you might expect, as the DOM extension does not provide a real method that takes care of that, explicitly. Instead we will have to use
a combination of DomDocument::insertBefore(). As an example, suppose we want to move our new url from above just before the very first url:

$xpath = new DomXPath($dom);
$xpath->registerNamespace("c","http://www.google.com/schemas/sitemap/0.84");
$result = $xpath->query("//c:url");
$result->item(1)->parentNode->insertBefore($result->item(1),$result->item(0));

echo $dom->saveXML();

DomDocument::insertBefore() takes two parameters, the new node and the reference node. It inserts the new node before the reference node. In our example, we insert the second url ($result->item(1)) before the first one ($result->item(0)).
I hear you asking why we use DomDocument::insertBefore() on the $result->item(1)->parentNode node.. Couldn't we just as easily use simply $result->item(0)? No of course not, as we need to execute DomDocument::insertBefore() on the root element, urlset, and not a specific url (look at our xpath query).
We could use the following code which is perfectly valid and gets us the same results, though:

$result->item(0)->parentNode->insertBefore($result->item(1),$result->item(0));

If we wanted to append the first url at the bottom of the sitemap, the following code is the way to go:

$result->item(0)->parentNode->appendChild($result->item(0));

// or $dom->documentElement->appendChild($result->item(0)); respectively

Easy is it not? :) DomDocument::insertBefore() and DomNode::appendChild() automatically move (and not copy and then move) the corresponding nodes. If you wish to clone a node first before moving it, use DomNode::cloneNode() first:

$source = 'sitemap.xml';
$dom = new DomDocument();
$dom->load($source);

$xpath = new DomXPath($dom);
$xpath->registerNamespace("c","http://www.google.com/schemas/sitemap/0.84");
$result = $xpath->query("//c:url");
$clone = $result->item(0)->cloneNode(true);
$result->item(4)->parentNode->appendChild($clone);
echo $dom->saveXML();

The important thing here is that you have to supply omNode::cloneNode() with a true parameter (default is false), so that it copies all descendant nodes as well. If we had left that to false, we would have gotten an empty <url></url> node, which is not desirable. ;)

Modifying Node Data

When modifying node data, you want to modify the CDATA within a node. You can use xpath again to find the node you want to edit and then simply supply a new value to its data property:

$source = 'sitemap.xml';
$dom = new DomDocument();
$dom->load($source);

$xpath = new DomXPath($dom);
$xpath->registerNamespace("c","http://www.google.com/schemas/sitemap/0.84");
$result = $xpath->query("//c:loc/text()");

$node = $result->item(1);
$node->data = strtoupper($node->data);

echo $dom->saveXML();

This code transforms the location data of the second url to uppercase letters.

Removing Data From XML Documents

There are three types of data that you would possbily want to remove from xml documents: elements, attributes and CDATA. The DOM extension provides a method for each of them:
DomElement::removeAttribute(), DomNode::removeChild() and DomCharacterData::deleteData(). We will use a custom xml document and not our sitemap to demonstrate their behavior. This makes it easier for you
to come back to this article and see at first glance how these methods work. Thank Nikos if you want to. ;)

$xml = <<<XML
<xml>
  <text type="input">This is some really cool text!</text>
  <text type="input">This is some other really cool text!</text>
  <text type="misc">This is some cool text!</text>
  <text type="output">This is text!</text>

XML;

$dom = new DomDocument();
$dom->loadXML($xml);
$xpath = new DomXPath($dom);

$result = $xpath->query("//text");

// remove first node
$result->item(0)->parentNode->removeChild($result->item(0));

// remove attribute from second node
$result->item(1)->removeAttribute('type');

//delete data from third element
$result = $xpath->query('text()',$result->item(2));
$result->item(0)->deleteData(0, $result->item(0)->length);

echo $dom->saveXML();

The output of this is:

< ?xml version="1.0"?>
<xml>
 
  <text>This is some other really cool text!</text>
  <text type="misc"></text>
  <text type="output">This is text!</text>

In this example we start by retrieving all text nodes from a document. Then we remove some data from that document. Simple.
In fact we remove the first node alltogether as well as the attribute of the second node. Finally we truncate the character data of the third node, using xpath to query the corresponding text() node.
Note that DomCharacterData::deleteData() requires a starting offset and a length parameter. Since we want to truncate the data in our example we supply 0 and the length of the CDATA node.

DOM And Working With Namespaces

DOM is very capable of handling namespaces on its own. Most of the time you can ignore them and pass attribute and element names with the appropriate prefix directly to most DOM functions.

$dom = new DomDocument();

$node = $dom->createElement('ns1:somenode');
$node->setAttribute('ns2:someattribute','somevalue');

$node2 = $dom->createElement('ns3:anothernode');
$node->appendChild($node2);

// Set xmlns attributes

$node->setAttribute('xmlns:ns1', 'http://php-coding-practices.com/');
$node->setAttribute('xmlns:ns2', 'http://php-coding-practices.com/articles/');
$node->setAttribute('xmlns:ns3', 'http://php-coding-practices.com/sitemap/');
$node->setAttribute('xmlns:ns4', 'http://php-coding-practices.com/about-the-author/');

$dom->appendChild($node);

echo $dom->saveXML();

The output of this script is:

< ?xml version="1.0"?>
<ns1 :somenode
  ns2:someattribute="somevalue"
  xmlns:ns1="http://php-coding-practices.com/"
  xmlns:ns2="http://php-coding-practices.com/articles/"
  xmlns:ns3="http://php-coding-practices.com/sitemap/"
  xmlns:ns4="http://php-coding-practices.com/about-the-author/">

  <ns3 :anothernode/>

We can simplify the use of namespaces somewhat by using DomDocument::createElementNS() and DomDocument::setAttributeNS(), which were specifically designed for this purpose:

$dom = new DomDocument();

$node = $dom->createElementNS('http://php-coding-practices.com/', 'ns1:somenode');
$node->setAttributeNS('http://somewebsite.com/ns2', 'ns2:someattribute', 'somevalue');

$node2 = $dom->createElementNS('http://php-coding-practices.com/articles/', 'ns3:anothernode');
$node3 = $dom->createElementNS('http://php-coding-practices.com/sitemap/', 'ns1:someothernode');

$node->appendChild($node2);
$node->appendChild($node3);

$dom->appendChild($node);
echo $dom->saveXML();

This results in the following output:

< ?xml version="1.0"?>
<ns1 :somenode
  xmlns:ns1="http://php-coding-practices.com/"
  xmlns:ns2="http://somewebsite.com/ns2"
  xmlns:ns3="http://php-coding-practices.com/articles/"
  xmlns:ns11="http://php-coding-practices.com/sitemap/"
  ns2:someattribute="somevalue">

  <ns3 :anothernode xmlns:ns3="http://php-coding-practices.com/articles/"/>
  <ns11 :someothernode xmlns:ns1="http://php-coding-practices.com/sitemap/"/>

Interfacing With SimpleXML

As I have mentioned at the start of our little DOM journey it is very easy to exchange loaded documents between SimpleXML and DOM. Therefore, you can take advantage of both
systems' strengths - SimpleXML's simplicity and DOM's power.

You can import SimpleXML object into DOM by using PHP's dom_import_simplexml() function:

$sxml = simplexml_load_file('sitemap.xml');
$node = dom_import_simplexml($sxml);
$dom = new DomDocument();
$dom->importNode($node,true);
$dom->appendChild($node);

DomDocument::importNode() creates a copy of the node and associates it with the current document. Its second parameter - a boolean value - determines if the method will recursively import the subtree or not.

You can also import a dom object into SimpleXML using simple_xml_import_dom():

$dom = new DomDocument();
$dom->load('sitemap.xml');
$sxe = simplexml_import_dom($dom);
echo $sxe->url[0]->loc;

Conclusion

DOM is certainly a very powerful way of dealing with XML documents. While it provides a good interface for basically every task one could dream of it often takes quite a lot of code lines to accomplish a task. SimpleXML's interface is of course a little easier, but less powerful.

Especially the fact that SimpleXML is rather incapable of removing data makes DOM the way to go for more complicated XML document processing. DOM's power in dealing with namespaces make it a valuable tool when dealing with large portions of data where naming conflicts are likely.

In fact we covered only a small portion of DOM's power. There are many other associating objects which have several useful methods. For example, we have not covered how to append character data. Check the DOM function reference for more information.

Thanks for staying with me on the DOM-boot till the end of our joirney! I hope you enjoyed it - please beware of the gap between the boot and the footbridge when leaving.

 

How I Turned A Slow Array Sort Into A Quick One Using The Quicksort Algorithmn

Posted on 31/5/07 by Tim Koschützki

Today in the morning I had a very unusual programming job to do - or at least what is for me rather unusual. I was confronted with the question whether it is easy to implement the quicksort algorithmn to sort an array of arrays based on a key in the second-dimension of the array. Join me to find out.

As you probably know from my About me page, I am currently working for LinkLift as a web programmer. The company is the largest German Textlink-Marketplace and soon we will expand to other countries within Europe. When you click on one of the link categories on our homepage, you are presented with (unfortunately German) website from which you can buy textlinks.

(Note: We are using the term “adspace” here for purchasable textlinks on a publisher's website.)

Using the links in the table header, you can sort the adspaces by title, available adspaces, page rank and current value.

Here is an array of adspaces, which we are willing to sort. The data is of course changed to preserve publisher anonymity.

[code]
Array
(
[0] => Array
(
[idv11] => 624059d1f8N0
[url] => http://example0.com,
[title] => Some title0,
[current_pagerank] => 7,
[adspots] => 2,
[current_available] => 1,
[links] => 33,
[links_external] => 5,
)
[1] => Array
(
[idv11] => 67059d1f8N0
[url] => http://example1.com,
[title] => Some title1,
[current_pagerank] => 4,
[adspots] => 4,
[current_available] => 3,
[links] => 132,
[links_external] => 9,
)
[2] => Array
(
[idv11] => 6759d1f8N0
[url] => http://example2.com,
[title] => Some title2,
[current_pagerank] => 2,
[adspots] => 8,
[current_available] => 6,
[links] => 12,
[links_external] => 3,
)
)
[/code]

The idv11 is the primary key. We decided to generate a random number for each new adspace so that our customers cannot guess how many adspaces we have in the marketplace by simply looking at their id. ;) The rest of the information should be straightforward - adspots is the number of textlinks the publisher offers on his site, current_available is the number of textlinks that can be purchased still, links is the number of links on the page where our textlinks will appear and links external is the number of those links that go to an external site.

The old sort algorithmn was rather slow and thus it was my task to optimize it. I tend to be used for optimisation tasks quite often these days.. Okay, so let's look at the old sort algorithmn:

function sortAdspacesAndUnify($adspaceArray, $sortBy)
  { 
    $orderBy = 'asc';
    if($sortBy < 0 && $sortBy != -1) $orderBy = 'desc';
    $sortUsBy = determineAdspaceSortBy($sortBy);
    if($sortBy == -1) $sortUsBy = 'revenue';
   
   
    $orderedArray = array()
    $usedIdv11s = array()
    $numResultArray = count($adspaceArray);
   
    while ($numResultArray > 0) {
      $latestKey = 0;
      $highestValue = 0;
      foreach($adspaceArray as $key => $adspace) {   
        if($sortUsBy == 'revenue')
          $value = LL_Admin::calcAdspaceConversion($adspace)
        else
          $value = $adspace[$sortUsBy];
                     
        if ($value >= $highestValue) {
          $highestValue = $value;
          $latestKey = $key;
        }
      }
     
      if (! empty($adspaceArray[$latestKey])
        && !in_array($adspaceArray[$latestKey]['idv11'],$usedIdv11s))
      {
        $orderedArray[] = $adspaceArray[$latestKey];
        $usedIdv11s[] = $adspaceArray[$latestKey]['idv11'];
      }
      unset($adspaceArray[$latestKey]);
     
      $numResultArray--;
    }
     
    if($orderBy == 'desc')
      $orderedArray = array_reverse($orderedArray);
             
    return $orderedArray;
  }

The function might seem a bit overwhelming to you at first, but in fact it's all quite easy. Let's explore the code a bit.

What the function receives is an array of adspaces. Each adspace itself is an array again to hold its information as presented earlier. The second parameter is an integer determining what to sort by. The function call $sortUsBy = determineAdspaceSortBy($sortBy); maps that integer to one of the following strings: "current_available", "link_value", "title" or "current_pagerank". Unfortunately, I cannot show you all of the code, but the person above me will not allow me to do so. It is not a problem, though, in my opinion.

If the $sortBy integer is negative we will sort in descending order. If it is -1, we will sort by adspace "revenue" – the default sortation for each category. Sorting by revenue basically means sorting by the product of current_value * (adspots – current_available). As you can see, revenue in this case means the amount of revenue an adspace has generated for the company already.

Next we declare two arrays - orderedArray, which will be our result array of sorted adspaces, and usedIdv11s, which allows us to track duplicated publishers just in case our input array contained some publishers more than once.

Now the sortation begins. What my colleagues were doing is simply iterating over the array and for each iteration iterate over all existing publishers again, determine the publisher with the highest value to sort by, track it in the ordered array and remove it from the publisher array.

What that means is the number of iterations is n + (n-1) + (n-2) + ... + 1 = (n2 + n)/2, where n is the number of adspaces of the input array. The average complexity class is therefore n2 and thus the the performance gets a lot worse with increasing numbers of adspaces. By the way, note that the $numResultArray variable gets decreased with every iteration. If we did the outer iteration via

while (count($adspaceArray) > 0) {

..we would have had another real performance bottleneck. Check my article Optimising For-Loops for more information on that one.

Okay, so now we are basically where I had been at 9:00am this morning. What I did at this point is turn to Wikipedia and searched for Quicksort, one of the fastest sorting algorithmns ever created. I simply remembered quicksort as being a fast sort algorithmn from my past computer science lessons at school. :) Quicksort is rather simple. It determines a random pivot element, then sorts the array so that all elements greater than the pivot are right from it and all which are less than the pivot's value are left to it. After such a sortation the pivot is at the correct place already and we have to sort the two sub-lists via the same approach.

Quicksort does that recursively and it is extremely fast, check the complexity and time analysis at the end of the wikipedia article there. I thought it will be a lot faster than the old publisher sortation algorithmn and off I went implementing quicksort. :)

What I turned up with is the following:

/**
     * Sorts an adspace array based on the quicksort algorithmn
     *
     * @author tim
     * @param array array of adspaces - note that each has to have an 'idv11' key
     * @param string value to sort by - each adspace element has to have such a key
     * @return array sorted array of adspaces
     */

  function quicksortAdspaces($q = array(), $sortUsBy, $level = 0, $orderBy='desc')
  {  
      $greater = array();
      $less = array();
      $pivotList = array();
     
      if (count($q) < = 1)
        return $q;
     
      $pivot = $q[0];
      foreach ($q as $adspace) {
        $value = '';
        $compareValue = '';
        if ($sortUsBy == 'revenue')
        {
        $value = LL_Admin::calcAdspaceConversion($adspace);
        $compareValue = LL_Admin::calcAdspaceConversion($pivot)
        } else {
        $value = $adspace[$sortUsBy];
        $compareValue = $pivot[$sortUsBy]
      }
       
        if ($value < $compareValue) $less[] = $adspace;
          if ($value == $compareValue) $pivotList[] = $adspace;
          if ($value > $compareValue) $greater[] = $adspace;
      }
      $return = array_merge(quicksortAdspaces($less,$sortUsBy, $level+1), $pivotList, quicksortAdspaces($greater,$sortUsBy, $level+1));
     
      if ($level == 0) {
        if($orderBy == 'desc')  $return = array_reverse($return);
        $return = cleanUpDoubleAdspaces($return);
      }
      return $return;
     }

Should not be difficult to understand. We go through all adspaces, determine a pivot by simply using $pivot = $q[0], which takes the first element of the array. Then we determine a value to sort by - either we sort by adspace revenue, which uses our revenue formula or we sort by one of the adspace's keys (current_pagerank, link_value, current_available or title). If the recursion level is zero, we clean up duplicate adspaces and reverse the array if we are to sort in descending order.

The result? The sortation now is lightning fast with an average complexity class of n * log2n, which is a lot better than n2.

Hope you liked my little story from today's morning. Feel free to modify and use this code. :)


 

Ten Simple Tricks That Will Make You A Valuable Colleague, Part I

Posted on 26/5/07 by Tim Koschützki

Being a productive and happy walker of the earth is what most of us aim at. Ever wanted to find out how you can quicken your programming? How you can earn a lot of money with your sites? How you can be more successful with women? How you can be a valuable colleague? Read the following ten simple tricks and find out how.

This is a two-part series. I will cover other aspects in the next part.

1. Keep Smiling And Entertain People

Smiling is probably the most important thing to be a success. Whenever and wherever you are - keep smiling. People like to go through life and just be happy. When you can get people to laugh and smile you are an easy guy to go with. They will perceive you as a happy person and will like to do business with you. Being a good entertainer can sometimes be as important as being a good coder.

2. Get Healthy And Exercise

You have to be your own secret weapon and your secret weapon needs a lot of energy and wealth. Be gentle to your body and stay healthy and wealthy. Here is a small list you should keep in mind:

  • Stop Smoking. This goes without explanation.
  • Don't eat rubbish. Refrain from fast food, fat food and candy. Eat a balanced diet with a lot of salad.
  • Exercise. 30 minutes a day is cool. Jogging, bike-riding, swimming or some hard stuff in the gym. Your body will love you for it. So will women.
  • Sleep. Your own secret weapon will need a lot of sleep. Eight hours a day is good. Try to go to bed always at the same time to make it easy for your body to keep track of its inner-clock.

3. Set Goals

Set a goal before starting anything. How can you tell if your are a success without goals? You need something to measure your success and with goals it will be easy. Either you full-filled your goals or you did not. Whenever you start something new - be it in the morning in the office or before you meet a friend - set a goal that determines what you want to achieve with what you are about to do. Writing down those goals is very valuable, too.

I for one have a Blog on my personal computer at home where I have things such as life goals, yearly goals, weekly goals and motivation stuff. This helps me maintain the bigger picture. When a new week starts I always break up the weekly goals and plan each day. I of course keep some space for additional tasks that come in unexpectedly leaving my weeks always well-planned. This makes it easy for others to count on me and it also allows me to remember other important things, like friends and going to the gym. What is your way of setting goals?

4. Measure Your Performance

That goes without saying. When you have goals you can measure if you achieved them. You should actively do so! It will help you find your own weaknesses and strengths and it will help analyse the reasons why you perform like you are performing. For example you will find out to which external distractions you are most vulnerable. Measure your results and quantify them whenever you talk to other people. This will not only sell yourself, but it will also help your teammates or customers to expect the rights thing from you.

If you told them you are "a great php programmer" they are completely lost. Most often they do not have the technical expertise to review that. However, if you told them you made your last client 15,000 bucks extra within the first month of the relaunch of his new website, people will be impressed. Measure your results and quantify. Then quantify some more.

5. Make Information Easily Accessible

You have to clean up your workspace and structure all information that you will need. Get away from thinking that people who oversee chaos are genius. It's just a dumb saying. Get clean, organized and have structure and goals with everything you do. You will be a success.

Are you about to make a new website? Have a list of Design Inspirations in your FireFox bookmarks! Do you want to be a friendly person that cares about his friends? Have a list of all your friends' and general business contacts' birthdays in your palm and be reminded of them. The send off gifts when people celebrate their birthdays. You will wonder how many of your friends or business partners will do that, too. This is an easy opportunity to stand out from the crowd.

6. Try To Be The Best-Dressed Person You See Today

Tim with Hat
People are so cursory when it comes to doing business with people. You have only seven seconds to make a great first impression. You need to be prepared for that! It's not only important for your PHP Programming - it's important for your overall success and personality. People will recognize that you take care when you dress and how you dress. There is not a hard rule as to how you should dress, it of course depends on the occasion. However, clean and shiny (light colors) are always good.

Watch your dressing - it will make you feel comfortable, self-confident and help you keep smiling.

Business Man
As you can guess, the person in the top left image is me. Probably not a very good photo that will sell my services. The person on the lower right photo is much better dressed and smiles, whereas I look almost evil. :)

As they say, dress for the job you want, not the job you have. - Paul Basar, Wildfire Games

7. Ask Yourself The Magic Question

Brendon Sinclair had a great productivity idea in his Webdesign Business Kit: He proposed that whenever you make first contact with a client you should ask yourself Why should the client do business with me?. The answer would be "To provide the best possible solution to his problem and make him a lot of money".

I am going a bit further and suggest that you ask yourself this question as often as possible - and not only with clients! For example - when you meet up with a woman ask the question. It will make you think about what the other person wants and expects from you. More so will it make you think why you are where you are and what to expect from the contact. It's something like reviewing your goals. It works like magic, try it out.

8. Keep In Contact

Now this is a classic one. Probably one of the most important things to keep yourself happy, productive and profitable. Keep in contact with everybody that could be of benefit to you, your business, your programming skills, your privacy life, etc. In my opinion it's the most essential thing to have good friends and contacts. They should be like you - smiling, setting goals, etc. You will all benefit from each other.

Having a couple of buds that drink beer all day long won't help your php career very much. Joining a PHP meet-up group will, however! Get out and actively seek the right people. You will be able to drink beer with them, too. ;)

Then keep in contact - at least once a month. Sending gifts or cards for things like anniversaries is always a good way to keep in contact. Keep in contact, then offer your services to friends and business partners. If they don't need your services at the moment they will at least ask around among their friends. However, only if you kept contact.

9. Regularly Work On Yourself

Sometimes people work too often in their business instead of on it. I think one should regularly work on one's knowledge and personality, not only to get better and better, but also to have a better way to measure your performance and eliminate the weaknesses. Here is a small list of what I consider important:

  • Learn A New Programming Language Every Year. For some of us, one year is even too long...
  • Read A New Programming-related Book Every Three Months.Our profession is a hard and ever-changing one. Keeping track of the latest technology and developments is crucial to success.
  • Regularly Read Relevant Feeds. There are a ton of cool sites with valuable content and feeds out there. Read them! Here is a list of feeds that I read regularly in .OPML format. You can easily import them into your own feed reader.
  • Read Non-programming Books. Spread your general knowledge and get interested in politics and all that kind of stuff. Small-talk is very crucial to selling your services and programming skills. However, with business partners (mostly intelligent people) you will not talk about the weather, but about sports events, politics, etc. Reading newspapers is good for you, too!

10. Use A PHP Framework

This is the first technical point I will make in this list. The second part will contain more technical tips.

Whenever possible, use a framework for your php projects. As you can guess, I highly recommend CakePHP, because it is very easy-to-use and has a steep learning curve. Even if you do not want to use Cake - do not reinvent the wheel with your programs. When you build an application from scratch you will have to worry about security, n-tier-architecture (Model-View-Controller and that stuff), internationalization, etc. all by yourself. Use a framework when possible. Period.

 
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50