March 2003 – XOOPS

This month, we looked at the surprisingly easy to install xoops2 from xoops.org. We are looking to see if we can help this project out by writing any modules you might think are missing, or by contributing to their on-line documentation. Read on for more about XOOPS!
We didn’t just talk about XOOPS either, we actually worked with it right there in the meeting. We walked through the install process, setting up modules and blocks, working with preferences and groups…everything you would need to setup your own XOOPS in a Saturday afternoon.

A couple of us are going to try working with a live install for a while to see if we get any ideas of things that are lacking. I’m hoping to finally get around to setting up that family website. And Doug was giving it a go as an intranet portal. Of course, with it’s integrated forum, we even talked a little bit about using it to run the pug. It would be nice not to have to log in twice on the site. 😉

Please check it out at http://www.xoops.org and offer any ideas or comments you would like to this article. What kind of site would you run? What kind of modules (image gallery, file repository, etc) would you need to run your site?

If you have access to a php install and a mysql database, XOOPS is easy to install into its own directory (I.e. you could run it in http://yoursite.com/xoops/ ) and not disturb your existing website. So if you find yourself with nothing to do some weekend, give it a try…I think you will be pleasantly surprised.

Until next time…

August – Templates Revisited

Well, its official…the Saturday experiment was a complete success! Thanks to all who attended! For those who didn’t you can read up on what you missed.

First off…my apologies on the delay. I finally have some time from my sister’s place in Maryland. Apparently I have to leave town to update the pug. 😉

Originally, we were going to extend on our Design Patterns talk by discussing the Article: Industrial Strength MVC by Jason Sweat. Instead, we mostly just focused on a re-introduction to the smarty templating engine.

We focused on the smarty crash course from the smarty site. We also hashed over the pros and cons of templates vs. includes vs. just plain embedded php.

If you are interested in templates in general, there is a very interesting blog entry Template Criteria written by Jason at phpcomplete.com. Smarty is only one of many, so if it is just too much for your project, feel free to look at others such as the PEAR template classes. If you have a favorite, be sure to add it as a comment!!

Great News!! We can be found at kcpug.org

Thanks to the generosity of Noah Dunker, you can now use http://kcpug.org to get to the site. I’ll be updating the apache config shortly to change the ServerName. You can continue to use the pug.jccc.net address, but kcpug.org is just so much cooler. THANKS NOAH!!

Noah also pointed out the dirt cheep regestration prices at godaddy.com Just incase you are like me and wanting to carve out some personal space on the web.

php{con West 2003

We also announced the php{con West 2003 (as you can see now on the site). If you go out tp their sponsors page, you can see the KC PUG add that Mark Zolton created for us. Thanks for the excellent and amusing take on our little pug.

Door Prize Giveaway

We also did our random drawing (a little more organized this time, mind you) and through pure luck of the draw Noah’s number was picked. He selected the copy of the PHP Cookbook donated by O’reilly’s User Group Program. Way to go Noah!

Final Time and Place

Last but not least, we of course discussed the new meeting time and place. I don’t want to ask php.net to move our time until we are sure…but after this last meeting….we seemed pretty sure. Please vote and comment with the poll on the main page.

That’s all for now. See you at the next PUG Meeting!!

July – Classes and Objects Revisited

The July meeting was by far the most fun I have had since we began. We sat down, introduced ourselves (there were plenty of new faces, even if most of the "regulars" didn’t show. 😉 ) and started talking about classes.

There was no formalized presentation, no stuffy classroom…just a pad of paper, lots of discussion and questions and a great time.

A Big Thanks to all of you who showed up. With any luck, we will have an even bigger turnout in August!!!

We did discuss times…and we might be able to try to push it up to 3:30. Would that help any of you make it? Please reply here.

May – Using the APD debugger

Far to long I have wanted a simple way to dump a call stack or retrieve
a simple profile. How long does this group of functions run? Which ones are
hit the most frequently? You know how it is…you use print statements
or logging to find your way. No longer…now there is a better way. Now, there
is APD: This month in PUG.

Caveats

I must clear the air first. There are a couple of things to keep in mind before
you try to use this.

Some things to keep in mind

  • Part of PECL (the C library counterpart to PEAR) but does not ship with PHP.
  • Being a compiled zend extension you will need access to your php.ini. This may be a problem if you are using a hosting provider
  • It will work on Windows, but you will most likely need MSVC. I can’t attest to this though…It does work well on RedHat Linux 8. 😉
  • Remember: as with all Zend Extensions, take care if you are running this on your production server. It will be getting intimate with your engine…and it is version 0.6. 😉

So, what does this do?

APD may save your life. It may feed your cat. It might even help you take our your land-lady’s garbage.

At the very least, php-apd WILL produce a trace of your running PHP script. That alone, is a big deal. In addition to a human-readable trace of your program’s execution, php-apd also comes with a “reporting” tool to digest the info for you.

Not quite following? Well, that’s what examples are for!

Here is a typical run for a pice of content where most of the dynamically generated content has already been cached. To get something similar, just put the following at the start of your program:

    apd_trace(99);

Then hit your page as usual. You will get a trace file, ready for analysis. Just point pprofp at it:

    $ pprofp -u pprof.01581
    Trace for /vol1/home/dholmes/site_html/www.remote-clean/admin/common.inc
    Total Elapsed Time =    1.14
    Total System Time  =    0.05
    Total User Time    =    0.79
    
    
             Real         User        System             secs/    cumm
    %Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
    --------------------------------------------------------------------------------------
     25.3  0.23  0.23   0.20  0.20   0.01  0.01   879   0.0002    0.0002       199816 mysql_fetch_array
     17.7  0.17  0.17   0.14  0.14   0.00  0.00  2239   0.0001    0.0001         3048 strlen
      7.6  0.07  0.07   0.06  0.06   0.00  0.00     5   0.0120    0.0120       701608 require_once
      5.1  0.02  0.02   0.04  0.04   0.00  0.00   192   0.0002    0.0002        51008 is_object
      5.1  0.04  0.04   0.04  0.04   0.00  0.00   114   0.0004    0.0004           40 is_resource
      3.8  0.08  0.08   0.03  0.03   0.00  0.00    53   0.0006    0.0006          568 mysql_query
      3.8  0.04  0.15   0.03  0.12   0.00  0.01    15   0.0020    0.0080       352048 include_once
      2.5  0.18  0.18   0.02  0.02   0.00  0.00   182   0.0001    0.0001        32616 sizeof
      2.5  0.01  0.01   0.02  0.02   0.00  0.00   183   0.0001    0.0001        -2760 is_array
      2.5  0.00  0.00   0.02  0.02   0.01  0.01    53   0.0004    0.0004          848 mysql_select_db
      2.5  0.04  0.28   0.02  0.23   0.00  0.02   871   0.0000    0.0003         6104 db_mysql->fetchrow
      2.5  0.02  0.02   0.02  0.02   0.01  0.01     2   0.0100    0.0100       278768 define
      2.5  0.01  0.01   0.02  0.02   0.00  0.00    79   0.0003    0.0003      -130896 get_class
      2.5  0.02  0.02   0.02  0.02   0.00  0.00   159   0.0001    0.0001         9776 preg_match
      1.3  0.01  0.01   0.01  0.01   0.00  0.00    53   0.0002    0.0002         2536 mysql_num_rows
      1.3  0.00  0.00   0.01  0.01   0.00  0.00   123   0.0001    0.0001        10128 preg_match_all
      1.3  0.01  0.25   0.01  0.21   0.00  0.02   881   0.0000    0.0002       148016 db_mysql->fetchinto
      1.3  0.01  0.01   0.01  0.01   0.00  0.00    23   0.0004    0.0004         2440 split
      1.3  0.00  0.00   0.01  0.01   0.00  0.00     1   0.0100    0.0100        -3408 resourcerendertoolsbox->getjs
      1.3  0.00  0.00   0.01  0.01   0.00  0.00    17   0.0006    0.0006         3272 method_exists
    

Not to shabby. We made 879 database calls…which seems a little excessive. But, it is MySql: The down and dirty fastest database on the planet (IMHO). Keep in mind, this is a pretty large framework that has never really had any optimizing. So, what do things look like when that same piece of content is not cached?

    $ pprofp -u pprof.01171
    
    Trace for /vol1/home/dholmes/site_html/www.remote-clean/admin/common.inc
    Total Elapsed Time =    5.04
    Total System Time  =    0.12
    Total User Time    =    3.46
    
    
             Real         User        System             secs/    cumm
    %Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
    --------------------------------------------------------------------------------------
     54.0  2.04  2.04   1.87  1.87   0.03  0.03  27917   0.0001    0.0001        25728 strlen
      5.8  0.24  0.24   0.20  0.20   0.00  0.00   958   0.0002    0.0002       268008 mysql_fetch_array
      4.6  0.11  0.11   0.16  0.16   0.00  0.00   686   0.0002    0.0002        65320 get_class
      2.9  0.31  0.31   0.10  0.10   0.00  0.00   825   0.0001    0.0001       102424 sizeof
      2.6  0.11  0.11   0.09  0.09   0.00  0.00  1317   0.0001    0.0001        45320 is_array
      1.7  0.06  0.06   0.06  0.06   0.00  0.00   137   0.0004    0.0004        37960 split
      1.7  0.02  0.02   0.06  0.06   0.00  0.00   415   0.0001    0.0001         5560 is_resource
      1.7  0.12  0.12   0.06  0.06   0.01  0.01   578   0.0001    0.0001         8936 file_exists
      1.4  0.04  0.04   0.05  0.05   0.00  0.00   329   0.0002    0.0002        13608 trim
      1.4  0.20  0.27   0.05  0.12   0.02  0.03    22   0.0023    0.0055       885720 include_once
      1.4  0.04  0.04   0.05  0.05   0.01  0.01     4   0.0125    0.0125       586632 require_once
      1.2  0.05  0.05   0.04  0.04   0.00  0.00   571   0.0001    0.0001       -61280 in_array
      0.9  0.80  0.80   0.03  0.03   0.01  0.01   156   0.0002    0.0002          776 mysql_query
      0.9  0.07  0.07   0.03  0.03   0.00  0.00   424   0.0001    0.0001        24600 preg_match
      0.9  0.03  0.03   0.03  0.03   0.00  0.00   156   0.0002    0.0002       -33720 db_mysql->modifyquery
      0.9  0.07  0.07   0.03  0.03   0.01  0.01  1028   0.0000    0.0000        72280 is_object
      0.9  0.04  0.29   0.03  0.26   0.00  0.00   871   0.0000    0.0003         6152 db_mysql->fetchrow
      0.9  0.00  0.28   0.03  0.11   0.00  0.00   327   0.0001    0.0003        -1192 sm_sitetag->getvar
      0.9  0.02  0.28   0.03  0.23   0.00  0.00   984   0.0000    0.0002       159352 db_mysql->fetchinto
      0.6  0.01  0.01   0.02  0.02   0.00  0.00   161   0.0001    0.0001         8632 mysql_escape_string
    

Oh, my! When I saw this the first time, my jaw almost hit the floor! I’m spending almost 2 full seconds calling STRLEN??? There has got to be something going on.

So, I found a problem…what now?

Well, you will need to fire up those deductive reasoning skills they pay you for: Ok, so I know that this is happening when the main pane of content is being generated since it isn’t showing up when that content is cached. I doubt I use strlen THAT many times…maybe if I do a recursive grep on my source tree I can look for something that may suggest something.

Well, it turned up one hit that may be causing it. My gut says that it isn’t related and sure enough it was a dead end. So, time to go the distance. Time to see the log.

If I generate a “calltree” from my log (which takes a very long time on a 2.5M log) I see the problem right away. The log is filled with little gems like this:

    $ pprofp -t pprof.01171 > calltree.txt
    
    --------SNIP---------
    db_mysql->getone
      settype
      sizeof
      db_mysql->prepare
        split
        strlen (371x)
        end
        key
      db->iserror
        is_object
      db_mysql->execute
    --------SNIP---------
    

All of my prepares are hitting strlen anywhere between 100 and 500 times!! Notice that I’m hitting strlen here 371 times, but I’m calling getone…which means I wanted something tiny RIGHT NOW. Tisk, tisk…it’s in PEAR_DB. Well, let’s crack it open and take a peak.

First I look in DB/mysql.php. However, there is not a prepare function defined in there…it must be in DB/common.php.

    // ------------SNIP--------------
    function prepare($query)
    {
        $tokens = split("[&?!]", $query);
        $token = 0;
        $types = array();
    
        for ($i = 0; $i !!!!!!!!!modifyquery
    

Not too bad for 5 minutes of work! (I opened a bug report the next day and it is now fixed, btw.) Of course, the next culprit is now reveled. Again, looking at the tree it is in the same function! This time, it’s the second half.

        $this->prepare_tokens[] = &$tokens;
        end($this->prepare_tokens);
        
        $k = key($this->prepare_tokens);
        $this->prepare_types[$k] = $types;
        $this->prepared_queries[$k] = &$query;
        
        return $k;
    

Which, brings us to the second half of performance tuning: Sometimes, you just can’t do a darn thing. Essentially, their prepare_tokens array only contains a few elements, but by the end of the program they are spaced pretty far apart. This makes end run for a while. It might be helpful to track the index of the last element…but I’ll cop out and just say “this ain’t my library.” 😉

It’s so cool! It must be impossible to install!

Not at all. Just follow the directions in the readme. It’s no more difficult to build and install (in UNIX anyway) than a simple little C program. 🙂

First untar it:

    $ tar -xzf apd-xxxx.tgz
    $ cd apd-xxxx
    $ phpize
    $ ./configure
    $ su -c "make install"  (as root)
    

You can use your phpinfo() to find your extension dir. If I recall I had to manually copy my apd.so by hand. So make sure it exists in your extension directory.

Next, open your php.ini. Again, use your phpinfo to make sure you have the right one.

Check the readme for the proper usage, but in my case I added:

    zend_extension = /usr/local/lib/php/extensions/no-debug-non-zts-20020429/apd.so
    apd.dumpdir = /tmp/dump_dir
    

Again, this is for my case only…your milage WILL vary.

Restart apache and poof! Add a comment if/how you got this running in windows. 😉

What about the callstack?

Perhaps one of it’s coolest functions for you development pleasure is apd_callstack (and the related apd_cluck and apd_croak). If I know it is blowing up somewhere, but I just can’t for the live of me figure out how it got there…you can have it die with a callstack. For instance, If I add the following line in some obscure module:

    apd_croak();

Then, what I get as output is a callstack just before it dies.

    croaked at /vol1/home/dholmes/site_html/www.remote-clean/admin/lib/catalog.inc line 204
    courses->courses() called at /site_html/www/admin/lib/catalog.inc line 142
    acadofferingsrender->_renderclasses() called at /site_html/www/admin/modules/catalog/AcadofferingsRender.mod line 106
    acadofferingsrender->modulethink() called at /site_html/www/admin/modules/catalog/AcadofferingsRender.mod line 258
    acadofferingsrender->run() called at /usr/local/lib/php/siteManager-2.2.0/lib/modules.inc line 93
    resourcerender->modulethink() called at /site_html/www/admin/modules/resource/ResourceRender.mod line 258
    resourcerender->run() called at /usr/local/lib/php/siteManager-2.2.0/lib/modules.inc line 434
    sm_layouttemplate->_runmodules() called at /usr/local/lib/php/siteManager-2.2.0/lib/layoutTemplate.inc line 152
    pagedefault->_runmodules() called at /usr/local/lib/php/siteManager-2.2.0/lib/codePlate.inc line 506
    sm_sitemanagerroot->completepage() called at /usr/local/lib/php/siteManager-2.2.0/lib/smRoot.inc line 69
    ???() called at //site_html/www/htdocs/home/catalog/index.php line 0
    

Oh, of course! It was getting there from the constructor method of the courses object being created by the acadOfferingsRenderer! Now, why didn’t I just see that? 😉 Interestingly enough, you can track the calls through our framework library right to the index.php script that was called in the first place…now that’s handy!!.

We can a few other related methods. All three are summarized below

    apd_croak       - Prints a callstack and dies.
    apd_cluck       - Prints a callstack with a warning.
    apd_callstack   - Returns a callstack as an array (for FANCY user-side formatting)
    

Some other status dumping functions (which work great inside a print_r, by the way) include:

    apd_dump_regular_resources    - Returns an array of resource types (oci8 statement, mysql link) indexed by resource number
    apd_dump_persistent_resources - Just about the same thing.
    dump_function_table           - Should be apd_dump_function table...
    override_function             - You guessed it, great for replacing php functions with "one liner" replacements
    rename_function               - Renames functions in the symbol table.  Oh, yeah.
    

Interactive Debugging

So, if you are not impressed yet…go get yourself a copy of the tcputils. Those of you with Debian or BSD probably already have them. Those of us RH users…have to pull them from here.

Once you have the tool tcplisten in your path, fire up an xterm and type:

    $ tcplisten -r localhost 7112

Or any other available port number that you wish.

Now, add the following somewhere towards the top of your source code:

    apd_set_session_trace_socket("127.0.0.1",APD_AF_INET,7112,99);

You should see it stream a bunch of stuff (if there is a lot more “running” to do” to your terminal. Nice, huh? It get’s better. This time, start up your term with the following:

    $ while tcplisten -r localhost 7112; do echo END OF SESSION...; done

and add a apd_breakpoint(1); to your php script.

You should get a rather dangerous command line into your php session. You can echo back values using apd_echo(), set variables in real time, call functions, output to the browser…all the evil things you may want to do. Just type apd_continue(1); to move your script again.

You can look at an example of using apd_breakpoint();

Just to point out, apd_echo() works in your program as well as a simple way to have your “debugging info” go to another window. This works well, but it may be easier to have a function like debugLog() that only calls apd_echo if the module is loaded. You wouldn’t want your production code blowing up when it gets to your non-apd server!

If the command line isn’t your thing, you can also try the PHP-GTK based php mole.

Cool Commands

Here are a couple of little helpers I recommend using:

    Does a standard profile on the most recent pprof dump (presuming you are in your dump directory)
      $ pprofp -u `ls -t pprof*  
      
    Generates your calltree off of the most recent pprof dump
      $ pprofp -t `ls -t pprof* | head -1 ` > calltree.txt
    
    Repeatedly opens a monitor console for your apd_set_session_trace_socket()
      $ while tcplisten -r localhost 7112; do echo END OF SESSION...; done
    

More Links

More Debuggers

This is just a sampling of the ones I bumped into…feel free to add more in the comments.

Happy Debugging!!!

April Meeting – PHPReports

This month we will about a little class for generating reports using php. If you are really interested in new and exciting ways to separate your data layer from your presentation layer…read on.

I was a bit pressed for time, so most of this content was taken either from the README or the phpreports site. The readme really is an excellent tour of the system, so if you are interested in how to use PHPReports, I would highly recommend you read it. Now, with that out of the way…

Requirements

You will need PHP compiled with XML/XSLT support. The PHPReports author uses the Sablotron libs in his work and examples.

Hello World

The PHPReports version of the classic Hello World, would be a simple table of database information. Lets take a look at the PHPReports Simple Sample before we get to far.

Document Structure

First off, we need to talk terminology. The author defines three key divisions or sections of a report:

  • The Document Layer
  • The Page Layer
  • The Group Layer

The author describes these best:

    You just have one document layer, one page layer (you can have a lot of pages, but just one page layer to configure) and some group layers.

    All these layers collect information about the data on your report, like the number of lines, statistics about the fields and so on.
    The document layer stores ALL these statistics, and stores it till the report end.

    The page layer stores it till the page end, and reset it there.

    The group layer stores it till the end of group, (where group is a set of data defined by a break expression, which could be any kind of field contained on your data set.)

    So, to visualize it in ascii art, we might use something like this:

    
        +----------------------------------+
        | DOCUMENT_LAYER                   |
        +----------------------------------+
        | HEADER                           |
        |                                  |
        | +------------------------------+ |
        | | PAGE_LAYER                   | |
        | +------------------------------+ |
        | | HEADER                       | |
        | |                              | |
        | | +--------------------------+ | |
        | | | GROUP LAYER              | | |
        | | | break expression A       | | |
        | | +--------------------------+ | |
        | | | HEADER                   | | |
        | | | FIELDS                   | | |
        | | |                          | | |
        | | | +----------------------+ | | |
        | | | | GROUP LAYER          | | | |
        | | | | break expression A,B | | | |
        | | | +----------------------+ | | |
        | | | | HEADER               | | | |
        | | | | FIELDS               | | | |
        | | | | FOOTER               | | | |
        | | | +----------------------+ | | |
        | | | FOOTER                   | | |
        | | |                          | | |
        | | +--------------------------+ | |
        | |                              | |
        | | FOOTER                       | |
        | +------------------------------+ |
        |                                  |
        | FOOTER                           |
        +----------------------------------+
    
    

    Or, if you prefer something a bit less abstract:

    
        +---------------------------------------+----+
        | John Doe Enterprises                  |////+--- document layer
        | Sales Report                          |////|
        +------+--------------------------------+----+
        | city | <city goes here>               |\\\\|
        +------+-+------+-------------+---------+\\\\|
        | id   | name   | product     | $       |\\\\|
        +------+--------+-------------+---------+\\\\+--- group layer
        | <id> | =name= | <product>   | <value> |\\\\|
        +------+--------+-------------+---------+\\\\|
        |                       total | <total> |\\\\|
        +-----------------------------+---------+----+
        |                  page total | <total> |////+--- page layer
        +-----------------------------+---------+----+
        |                report total | <total> |\\\\+--- document layer (again)
        +-----------------------------+---------+----+
    
    

    So what does my php script look like? It’s Massive right??

    
        include 'PHPReportMaker.php';
        $sql  = "select ID,NAME,CITY,PRODUCT,VALUE from SalesLog order by ID";
        $parm = Array();
        makeReport( "sales.xml", "PHPReport.xsl", "user", "mypass", 
                "mydb", "mysql", $sql, $parm );
    

    Or…If you prefer the “cleaner” OO form:

    
    <?php
        include 'PHPReportMaker.php';
        $sql  = "select ID,NAME,CITY,PRODUCT,VALUE from SalesLog order by ID";
        $oRpt = new PHPReportMaker();
    
        $oRpt->setXML("sales.xml");
        $oRpt->setUser("user");
        $oRpt->setPassword("mypass");
        $oRpt->setConnection("mydb");
        $oRpt->setDatabaseInterface("mysql");
        $oRpt->setSQL($sql);
        $oRpt->run();
    ?>
    
    

    sales.xml – The format spec for our report

    You know that that would just be too easy, right. Do you see the “sales.xml” string above? That points to an xml file that contains the format spec for our report. Think of it as a template for our data.

    
    <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
    <REPORT>
       <TITLE>Sales eport</TITLE>
      <BACKGROUND_COLOR>#FFFFFF</BACKGROUND_COLOR>
       <PAGE BORDER="1" SIZE="25" CELLSPACING="0" CELLPADDING="5" WIDTH="500">
          <HEADER>
             <ROW>
                <COL COLSPAN="5">John Doe Enterprises</COL>
             </ROW>
             <ROW>
                <COL COLSPAN="5">Sales Report</COL>
             </ROW>
          </HEADER>
          <FOOTER>
             <ROW>
                <COL ALIGN="RIGHT" COLSPAN="4">page total</COL>
                <COL ALIGN="LEFT" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
             </ROW>
          </FOOTER>
       </PAGE>
       <GROUPS>
          <GROUP NAME="maingroup">
             <FIELDS>
                <ROW>
                   <COL>id</COL>
                   <COL>name</COL>
                   <COL>city</COL>
                   <COL>product</COL>
                   <COL>value</COL>
                </ROW>
             </FIELDS>
          </GROUP>
       </GROUPS>
    </REPORT>
    

    This xml might show us a report that looks a lot like the following:

    Where’s the REAL code?? Some more complete examples

    There is a how-to example on the phpreports website that illustrates this topic. It also includes the complete XML file for generating the report.

    There are a host of other Samples available on their site.
    Be sure to check out their Super Swanky Report as well as the Debugging section. Yes, it has it’s own debugging helper.

February Meeting – XML_Tree/RSS

When people ask me for a list of “why PHP” I often include in my response that it has All of the Buzzwords. Simply put, if there is a reasonable web-related standard out there, chances are that there is class or something to work with it in PHP. XML may or may not be a buzzword, but whithout question PHP does support it.

Learn more about working with RSS and other XML types in this month’s PUG! Read on for more

The problem may be that PHP supports XML processing in a bunch of different ways but only one (or maybe two) of them ship standard. This can make it tough when you don’t run your own server

Also, I won’t be covering schema’s or DTDs, you can find info on those at the w3c website. What I do want to show are ways to work with XML that don’t require any additional compiling or loads of work!

As usual, when I want to work on something new,I look at what is in pear first. There are a ton of places for other classes out there (like phpclasses.org) but there is also (quite often) a whole lot of junk out there too

PHP ships with the expat libraries enabled these days for parsing XML. While it works… well, let’s just take a trip to the onlne doc, shall we…

Notice that it is event (or call-back) based. That’s all well and good…and see how easy it is to manipulate start tags into something and end tags into something….Hmmm, isn’t that what XSLT is for? Anyway, I’m not even going to cover this. I’ve used it…it’s in a production system, actually…but I had to keep track of state in class variables, etc…it was not pretty. Maybe there are better ways to do this, but it does take practice to wrap your arms around it. If it works for you, go for it…it is the fastest and most efficient way to parse XML.

One of the many who know what they are doing, has written an RSS parser using the expat extensions. Perhaps more accuratly, using the Pear XML_Parser which helps you write event parsers in a class.

RSS – A very popular XML application

As I understand it, RSS is a small part of the Resource Description Framework (RDF). Put simply, it is a way for sites to publish meta-data about their content for other applications (and sites) to use. While this has not cough on with many of the big sites, for fear of loss of revenue (cough, cough) or whatever…there are plenty of RSS feeds out there on the web.

First, let’s look at a simple RSS feed in the docs for XML_RSS.

Let’s start actually working through some other examples:

  • Here is a goofy little function to (poorly) draw out an html representation of the feed. Source: rss.php
  • And a little program to drive it. Source:simple.php
  • And finally, what we get when we run simple.php
  • What about other types of XML?

    Of course, working with a canned standard and a canned class is one thing, but what if we need to work with our own xml layouts?

    Just for the sake of example, we are going to write our own XML application. That’s application in the XML sense of the word. We are going to make up our own file format to hold the “feeds” array.

    Of course, we will take it a bit farther though, as XML is just so darn flexible

    So, let’s start with our Example XML file.

    First off, notice that we have kept the concept of “feeds”. These are just “groups” of RSS links. Notice, that we have even sprinkled some formatting data into our XML. This is generally BAD, but I’m just trying to give examples.

    Now, we are going to keep the same drawFeed() function that we used before…but we do want to try to swank it up a little, right? Also, we are going to use the pear library XML_Tree. This was written with the intention of creating an object-oriented XML writer for php. However, the author added the ability to read in an existing document and viola.

  • Take a look at the new code: Source: better.php
  • As well as the Output of better.php.
  • The new formatting (aka pretty stuff) is all handled by a few lines in the style.css.
  • For an easy listing of the demo files, just go to the doghouse

    Keep in mind, the XML_Tree library can be used for outputting XML in a clean (sorta) OO kind of way. Check out the docs for XML_Tree for more details.

    Happy PHPing!
    Dan Holmes

    January Meeting

    With the new year comes a new website, assorted info, and a way to speed up your use of frameworks. All inside this month’s PUG

    New year, new site: Powered by GeekLog

    I introduced the new PUG site…now powered by the geeklog site-in-a-can solution. I wanted to discuss some of it’s internals, available modules, etc…but instead just rambled a bit. 😉 There is already an intro article out there, so read that for more info if interested. After the meeting, Doug pointed out that codingtheweb.com has a portal-portal with RSS/RDF feeds. And, oh yes…they have a PHP feed (which I think is just stuff with the letters PHP in them). I added the RDF Portal box to the PUG site. Enjoy!

    Everyone is invited to write articles/comments/etc for our PUG…you can even submit one anonymously!

    Other Topics

    We had a general Q/A section and discussed things such as our favorite editors (VIM, komodo, ftpedit, and dreamweaver MX to name a few), some of PHP 4.3’s new features, using GD and ionCube’s PHPAccelerator.

    Image_Transform

    In answering a question, I introduced Image_Transform…a PEAR class for abstracting various image manipulation libraries. I have used it to scale images down on the fly but without the headaches involved with different GD versions or pixel math manipulation.

    This abstract factory style library provides member functions such as: resize, scale, scale to a max height/width, add Text and others. Having this in an abstraction layer allows you to switch libraries out depending on what is available on you system or based on what performs the best for your needs.

    For example, I had always assumed that GD’s imageCopyResampled would be the fastest way to size images on the fly. As it turns out, I actually got better performance letting Image_Transform use the mageMagik command line utilities.

    The ionCube PHP Accelerator

    We all know that PHP is bashed by “other” developers because a PHP script is usually compiled every time before it is run. When using single scripts or even small libraries, this compilation takes so little time it goes unnoticed. However, as you site grows, frameworks are implemented and your compile times start getting up there into the .5-.8 second range…you might start thinking that you need to invest in more hardware. This just usually isn’t the case.

    Sometimes, all you really need is something that will cache that compiled byte code. There are several alternatives available, such as the ones from Zend. However, as money is sometimes hard to find we have to go in search of other solutions. Enter ionCube and their PHP Accelerator

    There are some restrictions: It requires PHP 4.0.6 or higher (and you should too) and Apache 1.x in *nix, but if that is your setup…then enjoy!

    It also may not play with other Zend/PHP modules. For instance, the Komodo debugger did not work when I used it in the same server as PHPA. No big….I doubt that you will want to use it on a dev machine anyway as it can lead to a little “weirdness”. For instance, say you had an abstract class called Fruit and it is in a file called fruit.inc. Then you have a class called Apple which includes fruit.inc and extends Fruit. Now, all is good and the cache works great. Until, say, you add a member function to your base class: Fruit. PHPA will notice that THAT file has changed, and will recompile it. However, the functionality will not propagate down to the Apple class. Apple will still be the compiled version from before the functionality was added to it’s parent.

    If this doesn’t make sence…then you probably won’t have a problem with it anyway. 😉

    While I didn’t bother to write down exact numbers, when I was implementing this for our framework I noticed response times (from start of request to page render, using the advanced “time lynx -dump www.yourwebsite.com” command) were easily cut in half. Of course, I have written a fairly sloppy framework built on top of another big framework…but hey…like I said: in half. 😉

    Behind the scenes it uses apache shared memory segments and a directory on your filesystem to efficiently cache your PHP byte code. If your code wants a pick-me-up…this might be an afternoon alternative to a major rewriting effort.

    Happy Coding! And see you next month!