Dom Manipulation of SVG embeded inside XHTML

It’s been awhile now since the W3C proposed the XHTML + SVG + MAthML document type declaration and a certain level of support has been available for a while. I’ve been testing XHTML + MathML lately (see http://www.metonymie.com/apuntes/2008/05/14/probabilidad-formulas-basicas.html). Now Firefox 3 is out, and supposedly it has support not only for embedded SVG but also for Dom manipulation the SVG nodes ( Correction: Jeff Schiller has pointed out that SVG support has been available in Firefox since version 1.5 ). So this is a series of tests of the basics of working with SVG from Javascript.

Getting the content to be recognized by the browser.

While testing this on my machine, I found out that for the svg to be accepted inside the html I had to name the file with an .xhtml extension. Since the same document with a .html extension just wasn´t recognized by firefox (IE 7 didn´t recognize the embedded SVG one way or the other). An example: SVG embedded inline inside XHTML.

So that was fine for local tests, but what about a dynamically generated document?.

Well first we need to define the type declaration, and the appropiate namespaces:

	<?xml version="1.0"?>
	<!DOCTYPE html PUBLIC    “-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN”
	“http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd”><html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en”>
<html xmlns=”http://www.w3.org/1999/xhtml”
      xmlns:svg=”http://www.w3.org/2000/svg”
      xmlns:xlink=”http://www.w3.org/1999/xlink”
	  xmlns:mathml=”http://www.w3.org/1998/Math/MathML”>

Note: Declaring the namespace on the document level saves us from having to declare it in each and every instance of an SVG element. So out of laziness, I prefer to do it on the top of the document.

But this is not enough if we want the document to be recognized as XHTML, we need to either use the .xhtml extension or to use a header when serving the document with content type=”text/xml”. In php we can do it like this:

	header("content-type:text/xml");

An example: SVG embedded inline inside XHTML in a file with a non .xhtml extension.

You can also set the content type to a more specific definition, for example (inside the head of the document):

	<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8"/>

Using SVG inside XHTML

Now we’ll get into a little (just a little) more complex svg example that we’ll use for the rest of the post: Dom Manipulations of SVG Inside XHTML Examples

Defining the SVG elements

First we´ll create some basic SVG elements inside our document. Basically a Rectangle and a Circle inside a div.

	<div id="svg-content">
	<svg:svg height="300" width="700" is="svg-container">
	<svg:rect fill="#ff5500" height="50" width="200" y="100" x="300" stroke="#000000" stroke-width="2px"/>
    <svg:circle cx="150px" cy="100px" r="50px" fill="#ff0000" stroke="#000000" stroke-width="5px" id="circle"/>

	</svg:svg></div>

Accesing the SVG Objects Properties with Javascript

Getting the value of an SVG Object attribute

It is as simple as retrieving the element by ID and getting the attribute as we would for any other DOM object. In the example page, this is the code on the first button:

	alert( document.getElementById('circle').getAttribute('fill') );

Changing the value of an SVG Object Attribute

Again this is done as we would normally do with any other DOM Object.

	document.getElementById('circle').setAttribute('fill', '#ffdd22');

Creating and adding a new SVG Object

This is a little more complex since we have to create a document node with the SVG Namespace defined.

	function add_new_rectangle () {
		var atts = {"stroke-width":"1", "stroke":"blue", "fill":"yellow", "height":"20", "width":"40", "y":"100", "x":"220"};
		//Defining the SVG Namespace
		var svgNS = "http://www.w3.org/2000/svg";
		//Creating a Document by Namespace
		var node = document.createElementNS(svgNS, "rect");
		//Setting attributes by namespace
		node.setAttributeNS(null, "id", "new-rect");
		for(name in atts) {
			node.setAttributeNS(null, name, atts[name]);
		}
		var cont = document.getElementById(”svg-container”);
		//Appending the new node
		cont.appendChild(node);
	}

Using innerHTML with SVG

Now, a very simple way to deal with elements in HTML with the dom, is to use the innerHTML property. Luckily we can do the same with Divs containing SVG. Code for the fourth button in the example:

	alert(document.getElementById('svg-content').innerHTML);

And of course we can manipulate and change it with a new SVG element declaration. The function that calls the fifth button:

	function change_innerHTML_to_a_new_SVG() {
		var svg_str='‘;
		for(var x=1; x<10; x++) {
			svg_str += '<svg:circle cx="' + (x*30) + 'px" cy="100" r="' + (x*5) + 'px" fill="#ff' + (x*10) +  '00" stroke="#000000" stroke-width="3px"/>';
		}
		svg_str += '</svg:svg>';
		var cont = document.getElementById("svg-content");
		cont.innerHTML = svg_str;
	}

Javascript Global Variables Weirdness

This is a simple test of the use of Global variables in Javascript. Now suppose the following code writing outside of any function, class or whatever.


k = "k";

var t = "t";

alert("gl: " + k);

alert("gl: " + t);

Will alert us of both variables with those nice little javascript alerts

Now suppose we add this function to the code above


function test() {

	alert("t: " + k);

	alert("t: " + t);

	j = "J";

	var l = "l";

	alert("t: " + j);

	alert("t: " + l);

	k = "2k";

	t = "2t";

}

Now we run the test.


test();

which Alerts all the variables.

But now if we try the following:


alert("gl after test: " + k +" - " + "gl after test: " + t);

Alerts us with the following line gl after test: 2k - gl after test: 2t. So Globals can be modified from within functions. But what happens to the variables that were defined within the function?


 alert("gl: " + j);

//alert(”gl: ” + l); //This won’t run, l was scoped within test and can’t be accessed outside of test.

Conclusions: Variables initialized outside a function are global whether you use var or not. Variables initialized inside a function are not global if they are initialized with var, else they are global.

Using PDO in PHP 5 with MYSQL

Installation

You should first refer to the installation guide in PDO Installation.

In my particular case since I was intending to use this with the MySQL database, the configuration options I had to add where:
'--enable-pdo=shared' '--with-pdo-mysql=shared,/var/production/mysql' '--with-sqlite=shared' '--with-pdo-sqlite=shared'

And I also had to add the following lines to my php.ini file:
extension=pdo.so
extension=pdo_mysql.so

Testing the installation


//How to get which drivers are available for PDO
print_r( PDO::getAvailableDrivers() );

which shows
Array ( [0] => mysql )

Starting a PDO Connection

 

	//Starting a PDO Connection
	try {
		//Please fill this in with appropriate data for your Mysql Database, User and User Password.
		$dbh = new PDO('mysql:host=localhost;dbname=test', 'test_user', 'test_user_password');
	} catch(PDOException $e) {
    	echo $e->getMessage();
	}
	print_r($dbh);

Which shows
PDO Object ( )

You might get an exception there if you don set up your data ok, or if there is no driver for your DB engine.

For example if I try the following


	//Starting a PDO Connection
	try {
		//Sending the wrong Password.
		$dbh = new PDO('mysql:host=localhost;dbname=test', 'test_user', 'invalid_password');
	} catch(PDOException $e) {
    	echo $e->getMessage();
	}

I get SQLSTATE[28000] [1045] Access denied for user ‘test_user’@'localhost’ (using password: YES)PDO Object ( ), so you should use try catch and write appropriate code for the event of the PDO connection failing.

Simple PDO Operations

For the purpose of this examples I’m setting up the following Table in a DB called Test, and I’m inserting some basic data.


 Create database test
 create database test;
 use test;
 CREATE TABLE `test_one` (
    `test_oneID` int(10) unsigned NOT NULL,
    `test_char` varchar(127) default NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `test_one` VALUES (1,'aaaa'),(2,'bbbb'),(3,'cccc'),(4,'dddd'),(5,'eeee');

Executing a simple statement


	//Preparing the statement
	$stm = $dbh->prepare("Select * from test_one");
	//Simple execution of the statement
	$stm->execute();
	print_r( $stm );

Shows PDOStatement Object ( [queryString] => Select * from test_one )

Getting the row count


	//Getting row count of the statement
	$count = $stm->rowCount();
	echo("Count: " . $count . "
");

Shows Count: 5

Iterating through the rows of the PDO statement.


	//PDO::FETCH_ASSOC means it fetches an associative array.
	while ( $row = $stm->fetch( PDO::FETCH_ASSOC ) ) {
		print_r($row);
		echo("
");
	}

Which gives:


Array ( [test_oneID] => 1 [test_char] => aaaa )
Array ( [test_oneID] => 2 [test_char] => bbbb )
Array ( [test_oneID] => 3 [test_char] => cccc )
Array ( [test_oneID] => 4 [test_char] => dddd )
Array ( [test_oneID] => 5 [test_char] => eeee )

It is very important to close the cursor to free up resources.


$stm->closeCursor();

Fetching statements into objects


	$stm = $dbh->prepare("Select * from test_one");
	//Simple execution of the statement
	$stm->execute();
	//PDO::FETCH_ASSOC means it fetches an Object.
	$stm->execute();
	while ( $row = $stm->fetch( PDO::FETCH_OBJ ) ) {
		print_r($row);
		echo("
");
	}
	$stm->closeCursor();

By using PDO::FETCH_OBJ we are retriving the data to a stdClass Object, which is PHP’s base class for Anonymous Objects

what we get in return then is:


stdClass Object ( [test_oneID] => 1 [test_char] => aaaa )
stdClass Object ( [test_oneID] => 2 [test_char] => bbbb )
stdClass Object ( [test_oneID] => 3 [test_char] => cccc )
stdClass Object ( [test_oneID] => 4 [test_char] => dddd )
stdClass Object ( [test_oneID] => 5 [test_char] => eeee )

Extracting to a predefined object

First we define a Mock object, and then we set the fetch mode to this object.


	//A Mock Object used for testing
	class Test {
		private $test_oneID;
		public $test_char;
		public $another_att ="predefined value";
		static $static_att = "An Static Attribute";
	}

	$stm = $dbh->prepare("Select * from test_one");
	$stm->execute();

	//Determining the Class to be fetched to.
	$stm->setFetchMode( PDO::FETCH_CLASS, 'Test');
	$row = $stm->fetch( PDO::FETCH_CLASS );
	print_r($row);
	$stm->closeCursor();

Which gives us the data of the first row inserted into an instance of our mock object:


Test Object
(
    [test_oneID:private] => 1
    [test_char] => aaaa
    [another_att] => predefined value
)

It is important to remark that there are 2 ways to do this

we could have done the same thing by changing:

$row = $stm->fetch( PDO::FETCH_CLASS );

to:

$row = $stm->fetchObject('Test');

Fetching a set of rows to a predefined object

We could have also fetched the full set of results to our mock class.


	$stm = $dbh->prepare("Select * from test_one");
	//For fetching a set of rows to objects.
	$stm->execute();
	while( $row = $stm->fetchObject('Test')) {
		print_r($row);
		echo("
");
	}

Which will give us:


Test Object ( [test_oneID:private] => 1 [test_char] => aaaa [another_att] => predefined value )
Test Object ( [test_oneID:private] => 2 [test_char] => bbbb [another_att] => predefined value )
Test Object ( [test_oneID:private] => 3 [test_char] => cccc [another_att] => predefined value )
Test Object ( [test_oneID:private] => 4 [test_char] => dddd [another_att] => predefined value )
Test Object ( [test_oneID:private] => 5 [test_char] => eeee [another_att] => predefined value )

Binded PDO Statements

One of the big advantaged of PDO is the use of Binded Statements, because:

  • They prevent SQL Injections
  • They generally mean a huge speed increase, because of the preparation of the statement in the DB before execution.

A simple example


	$stm2 = $dbh->prepare("Select * from test_one where test_oneID = :test_oneID");
	$stm2->bindValue(":test_oneID", 3);
	$stm2->execute();
	echo("
");
	$row = $stm2->fetchObject('Test');
	print_r($row);

Which returns:


Test Object ( [test_oneID:private] => 3 [test_char] => cccc [another_att] => predefined value )

Dealing with statement errors

Now let’s suppose the following code, where we are trying to fecth data from a non existent table:


	$sth = $dbh->prepare("Select * from non_existing table");
	$sth->execute();
	$row = $sth->fetch( PDO::FETCH_ASSOC );
	print_r($row);

Well, this code will produce no result at all. To access the PDO Statement error, we must explicitly address it.


	$arr = $sth->errorInfo();
	print_r($arr);

Which will show:

Array ( [0] => 00000 [1] => 1064 [2] => You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘table’ at line 1 )

Setting PDO to treat PDO Statements errors as exceptions

With the following line, we can set up the PDO object to treat all PDO Statements as Exceptions


$dbh->setAttribute(	PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Now we can deal with statement errors as exceptions, for example:


	$dbh = new PDO('mysql:host=localhost;dbname=test', 'test_user', 'test_user_password');
	$dbh->setAttribute(	PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	$sth = $dbh->prepare("Select * from test_onedfgsd");
	try {
		$sth->execute();
	} catch ( Exception $e) {
		var_dump($e);
	}

Which will give us:


object(PDOException)#3 (7) {
  ["message:protected"]=>
  string(92) “SQLSTATE[42S02]: Base table or view not found: 1146 Table ‘test.test_onedfgsd’ doesn’t exist”
  ["string:private"]=>

  string(0) “”
  ["code:protected"]=>
  string(5) “42S02″
  ["file:protected"]=>
  string(36) “/home/www/ttiw/article.PDO_tests.php”
  ["line:protected"]=>
  int(231)
  ["trace:private"]=>
  array(1) {
    [0]=>
    array(6) {
      ["file"]=>

      string(36) “/home/www/ttiw/article.PDO_tests.php”
      ["line"]=>
      int(231)
      ["function"]=>
      string(7) “execute”
      ["class"]=>
      string(12) “PDOStatement”
      ["type"]=>
      string(2) “->”
      ["args"]=>
      array(0) {
      }
    }
  }
  ["errorInfo"]=>

  array(3) {
    [0]=>
    string(5) “42S02″
    [1]=>
    int(1146)
    [2]=>
    string(40) “Table ‘test.test_onedfgsd’ doesn’t exist”
  }
}

Well that pretty much wraps up the basics of PDO. As a side note I would suggest that instead of using try-catch on every PDO Connection or PDO Statement, to better use a higher level exception handler and write some code for PDO Exceptions. For how to achieve this see set_exception_handler PHP Function.

Simple “Days Iteration” Algorithm with a twist

Yet another one of this date iterations algorithms, due to the particulars of the script I was running I had to redo the date iteration to start on one date and iterate through all the days till another one. This is the new version, (that I’m blogging for not having to go through this again in the future):



begA = 2001;
begM = 3;
begD = 1;
endA = 2007;
endM = 1;
endD = 1;

itA = begA;
itM = begM;
itD = begD;
while( ((itA!=endA) || (itM!=endM) || (itD!=endD)) ) {
	//Print
	document.write(itA + '-' + itM + '-' + itD + '');
	if(itM==12 && itD==31) {
		//End of Year
		itA++;
		itM=01;
		itD=01;
	} else if(
		//End of Month
	( (itM==1 || itM==3 || itM==5 || itM==7 || itM==8 || itM==10 ) && itD==31)
 	//Months with 31 days
	|| ( (itM==4 || itM==6 || itM==9 || itM==11 ) && itD==30)
	//Months with 30 days
	|| (itM==2 && itD==28 && (itA%4!=0))
	//February not leap
	|| (itM==2 && itD==29 && (itA%4==0))
	//February leap
	) {
		itM++;
		itD=01;
	} else {
		itD++;
	}
}

Simple “Days Iteration” algorithm

I had to do a script for iterating through a set of MySQL fields of type Datetime. Since I’ve run through this before, I’m blogging the litle thing for rememberance in the future. Here is the a version in Javascript.



for(var a=2001; a <2007; a++) {

 for(var m=1; m<13; m++) {

 	var monthdays = 31;

 	if(m==4 || m==6 || m==9 || m==11) {

 		monthdays = 30;

 	}

 i	f(m==2) {

 		if( a%4 == 0 ) {

 			monthdays = 29;

 		} else {

 			monthdays = 28;

 		}

 	}

 	for(var d=1; d< (monthdays+1); d++) {

 		document.write(a + ' ' + m + ' ' + d + '<br/>');

 	}

 }

}

Using Fulltext Search on MySQL

Fulltext indexes improve the performance of queries from text or long varchar fields, compared to using like or Rlike they are way faster. They also provide more intelligence to searching by matching and sorting not just by existence of a word in the field but by considering the weight of the word in that text ( my guess on the meaning of this is actual count of the word in the text divided by total words in the text). A more comprehensive explanation of MySql’s implementation of fulltext search can be accessed here (I’m linking to google’s cache since the original presentation is not accessible anymore). There is an interesting explanation there of how the index is actually created.

Basic Syntax

To query a table by full text search firts the field needs to be indexed as fulltext:


Alter table mytable add Fulltext mytable_fieldToSearch( fieldToSearch );

To do a basic search the syntax is the following:


Select elemID from fieldToSearch where Match( fieldToSearch ) against("word1 word2") limit 10

Considerations

  • When searching by fulltext search it’s not posible to add other conditions to the select statement.
  • It’s important to add a limit to the query
  • To further improve performance (specially when searching within multiple fulltext indexed fields) is to create an intermediate table with just the ID of what we are searching for and the different fields of text concatenated into one, and then querying that table directly.
  • It may also be important to collate the database field to a character set (I’m writing this to remember in the future):
    
    ALTER TABLE mytable DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE=utf8_general_ci;
    

Static (Class Definition) attributes on PHP 5

Static attributes (or methods) can be considered class definition attributes (or methods). They are not accessed from a Class instance but directly from the definition of the class itself, as such they can be accessed directly using the Class_Name::$attribute_name syntax or within the class by the Self::$attribute_name syntax.

The following are a couple of tests of this.

We create a simple class with an Static Attribute


class Static_Test {
	public static $counter = 0;
	function show_counter() {
		echo self::$counter;
	}
	function increase_counter() {
		self::$counter++;
	}
}

Accessing the static attribute from the class definition


echo Static_Test::$counter;

Results: 0

Accessing the static attribute within a method from an instance of the Class


$obj1 = new Static_Test;
$obj1->show_counter();

Results: 0

Modifying the attribute value


Static_Test::$counter++;
$obj1->show_counter(); //1

Results: 1


// $obj1->counter++; //Does Nothing.

Results: nothing. Static Attributes can not be accessed directly, even when declared as public.


$obj1->increase_counter();
$obj1->show_counter(); //2

Results: 2. They can be accessed within methods if the syntax used in the method is appropiate.

Accessing the static attribute within a method from another instance of the Class


$obj2 = new Static_Test;
$obj2->increase_counter();

$obj1->show_counter(); //3

Results: 3. Changes done to a static attribute on an instance are reflected across all instances of the class.

A graphical representation of what we’ve done

Class Definition Attributes Graph

Accessing Static Attributes from a Child Class


class Static_Child_Test extends Static_Test {
	function increase_parent_counter() {
		parent::$counter++ ;
	}
}
$obj3 = new Static_Child_Test;
$obj3->increase_parent_counter();

$obj2->show_counter(); //4

Results: 4.

Accessing Static Methods

The same holds true for static methods. An example:


class Static_Test2 {
	public static function static_method() {
		echo 'This is an static Function. Called directly from the Class Definition and not from an Instance of the Class.';
	}
}

Static_Test2::static_method();

Notes on Attribute Visibility on PHP 5

A couple of years ago when I was studying PHP 5 OOP, I did a couple of scripts to test the different aspects of PHP’s particular object implementation. Since I’m currently migrating a whole bunch of PHP 4 scripts to PHP 5, I’ve decided to consult my notes, which I’m presenting here, plus some explanations for extra clarity.

This are my notes on Attributes and Method visibility within Objects.

First we create a simple class with an attribute (variable) of each type of visibility:


class MyClass {
	public $public = 'public';
	protected $protected = 'protected';
	private $private = 'private';
	function printHello() {
		echo $this->public; echo ', ';
		echo $this->protected; echo ', ';
		echo $this->private;
	}
}

Accessing Attributes Directly


$obj = new MyClass;
echo $obj->public; //Works
//echo $obj->protected; //Fatal Error
//echo $obj->private; //Fatal Error

$obj->printHello(); //Prints Everything.

Access Conclusions

Public attributes can be accessed directly.

Protected and Private can not.

Accesibility in Child classes


class MyClass2 extends MyClass {
	function testPublic() {
		echo $this->public; //Prints "Public".
	}
	function testProtected() {
		echo $this->protected; //Prints "Protected".
	}
	function testPrivate() {
		echo $this->private; //Prints nothing.
	}
}
$obj2 = new MyClass2;
$obj2->printHello(); //Prints everything.
$obj2->testProtected(); //Prints "Protected".
$obj2->testPrivate(); //Prints nothing.

Conclusions

Public and Protected attributes can be accessed in child objects.

Private can not.

Redeclaring (or Overriding ) attributes in Child classes


class myClass3 extends myClass {
	public $public = 'new public value';
	protected $protected = 'new protected value';
	private $private = 'new private value';
}
$obj3 = new MyClass3;
$obj3->printHello(); //Prints "new public value, new protected value, private"

Conclusions

Public and Protected attributes can be redeclared in child objects.

Private can not. If redeclared, the redeclaration will not take effect.

Final Conclusions

Note: The same holds true for object methods.

Encapsulation is the reason behind the separation of methods and attributes by levels of visibility. When refactoring an object, Private methods and attributes can be modified freely since they are only accesible within the object and never directly in the scripts or by inheritance in another objects. Protected makes things a little more complex since even though they can not be accessed directly, they can be accessed, redeclared or overriden by child classes. Lots of care needs to be taken when refactoring Public methods or attributes.

Notes on Object Assignment under PHP 5

This is an extended explanation of the examples on Object Assignment (example 19-5) from OOP5 Basics on the PHP Manual.

The full script can be accessed here

First we create the simple class from the example.


class SimpleClass {
	//member declaration
	public $var = 'default value';
	//method declaration
	public function displayVar() {
		echo $this->var;
	}
}

Creating a New Instance


$instance = new SimpleClass();

What exactly does this do?. It Allocates an space in memory for this new object and then references the variable $instance to this space in memory (representing the instance of the object).

Instance equals new object Graph

Assigning a Variable to an already created object


$assigned = $instance;

References the variable $assigned to the object in memory referenced by $instance.

Assigned equals instance Graph

Referencing a Variable to a variable that references an object


$reference &= $instance;

References the variable $reference not to the object in memory referenced by $instance but to the variable $instance itself.

Reference references instance Graph

Cloning an object


$cloned = clone $instance

Allocate in memory space for a new instance of the Object. Copy all the values from the instance referenced by $instance. References the variable $cloned to this new instance of the object in memory.

Cloned clones instance Graph

Full Picture up to now

If we dump with var_dump all of this (see script). We get the following:

Instance: object(SimpleClass)#1 (1) { ["var"]=>  string(13) “default value” }
Assigned: object(SimpleClass)#1 (1) { ["var"]=> string(13) “default value” }
Referenced: object(SimpleClass)#1 (1) { ["var"]=> string(13) “default value” }
Cloned: object(SimpleClass)#2 (1) { ["var"]=> string(13) “default value” }

A good graphical representation of this is:

PHP 5Object Assignment Script State 1

Changing the value of a Member and then Dereferencing $Instance

Now, we’ll do a couple of changes so as to make all of this even clearer.


//Change variable in instance (will be changed in all but cloned)
$instance->var = 'New value';

//Derefence $instance.
$instance = NULL;

What we have just done is change the value of the member var in Obj 1 and then derefencing $instance to obj1.

Instance equals null Graph

Full Final Picture of the script

Let’s see what happens now when we dump all of the variables (see script). Results:

Instance: NULL
Reference: NULL
Assigned: object(SimpleClass)#1 (1) { ["var"]=> string(9) “New value” }
Cloned: object(SimpleClass)#2 (1) { ["var"]=> string(13) “default value” }

A graphical representation of the final state of our script:

PHP 5Object Assignment Script State 2

hCard_xtract v0.0.2 released

I have just finished version 0.0.2 of my hCard Extract Application. The changes on this new version are:

  • added support for FN ORG Optimization
  • added support for mail type
  • added support for multiple nicknames and for nickname inside n
  • added support for multiple categories

I’ve also decided to release the hCard Parser Library under the LGPL, source code can be accessed here. This is quite probably the last release of this, since I would like to do a full production version using Tidy under PHP 5 (that gives an HTML parser quite similar to the one I was using).

Post Archive

Post Categories

Search Posts