Posts tagged ‘PHP’

Merging associative arrays in PHP

It’s nice when serendipity is your friend! I was porting my Bitly class from Pike to PHP – I know there’s probably a hundred PHP classes already out there, but mine is better coded ;) – and noted by accident that I had used some Pike syntax in my PHP class but it was working anyway. So what was I doing? In Pike there’s separate data type for associative arrays called mapping. In Pike, in general, when merging two objects you just join them with a + sign. Thus merging two mappings you do like

9 lines of Pike
  1. mapping m1 = ([ "key1" : "Value 1", "key2" : "Value 2" ]);
  2. mapping m2 = ([ "key3" : "Value 3" ]);
  3. write("My mapping: %O\n", m1 + m2);
  4. //> My mapping: ([ /* 3 elements */
  5. //> "key1": "Value 1",
  6. //> "key2": "Value 2",
  7. //> "key3": "Value 3"
  8. //> ])

And I noted that I had done the same thing in PHP and the result was perfectly valid:

11 lines of PHP
  1. $a1 = array("key1" => "Value 1", "key2" => "Value 2");
  2. $a2 = array("key3" => "Value 3");
  3. echo "My mapping: ";
  4. print_r($a1 + $a2);
  5. //> My mapping: Array
  6. //> (
  7. //> [key1] => Value 1
  8. //> [key2] => Value 2
  9. //> [key3] => Value 3
  10. //> )

This method doesn’t work on flat array though so there you’ll still have to use array_merge(), but pretty nice anyway.

And oh, the PHP Bitly class will be part of the new PLib release once done!

PLib, the Poppa PHP Library

Other than working my ass off at work I just lately took a real approach on my secretly sleeping project PLib. PLib is my own PHP framework with functions and classes to ease my everyday PHP programming.

To give you an idea of what PLib is all about let me give you an example:

18 lines of PHP
  1. <?php
  2. require_once '/path/to/PLib.php';
  3. //! Lets create thumbnails of all JPEG images in a directory
  4. PLib::Import('Graphics.Image');
  5. //! The Dir class is included in Image so I don't explicitly need to
  6. //! include it
  7. $dir = new Dir('/path/to/jpegs/');
  8. $mw = 150; // Max width of thumb
  9. $mh = 90; // Max height of thumb
  10. while ($file = $dir->Emit('*.jpg')) {
  11. $img = new Image($file->path);
  12. $img = $img->Copy($file->path.'/thumb-'.$file->name)->Scale($mw, $mh);
  13. wbr($img->Path()); // The full path to the thumbnail
  14. }
  15. ?>

Rather easy I think!

AutoDoc

Another cool feature (if I may say so) is the auto documentation. If you quickly want a list of all available classes and functions just type PLib::Info() and you will get the list. Each class and function is then clickable and will lead you to the documentation for which ever class or function you clicked.

If you want the documentation for a specific class just type PLib::Info() and there you have the documentation.

The third way to use the autodoc is to pass an instance of a class as argument like PLib::Info().

And if you wan’t a full documentation site just type PLib::Info() and you will get the same documentation as available at the PLib site.

The site

Anyway, PLib has its site of it’s own where anyone curious can download the latest version of PLib and find out more about the project.

plib.poppa.se

PHP documentation browser

Even though I have my vacation right now I need to hack some ;)
What I have done is a PHP documentation browser, I simply call it PHPDoc Browser, for GNU/Linux to read the PHP documentation locally on the computer (it’s a little bit like CHM for Windows). This little app isn’t that necessary but I thought it was a great thing for learning new stuff: The application is written in Mono/C# and uses SQLite for storing the search index.

Difference from CHM

Although there’s a CHM equivalent, XCHM, for Unix like systems I decided that my application serves a purpose: What I like about the PHP manual is that you can hit www.php.net/the_function in you browser and you will get the documentation for “the_function”. Of course I implemented the same functionality in PHPDoc Browser. You can also do a wild card search like “array_*” and you will get a list of all “array_*” functions.

I also implemented a free text search but that’s not “a real” FTS at the moment. FTS in SQLite in a little bit harder than in MySQL so while awaiting the next SQLite version the FTS is a bit of a hack. But you can quote the search to narrow it down ;)

What’s learned?

Most of the unnecessary stuff I do I do to learn and the PHPDoc Browser is no exception. I learned how to use SQLite in Mono/C# and I also wrote a self contained installer in Bash, and Bash I havn’t really touch although I’ve been using GNU/Linux for 6-7 years! I found it quite fun writing the installer :)

Howto

If you would like to try it out:

  1. Download the install script
  2. Un-tar it (`tar zxvf phpdocbrowser-installer.tgz`)
  3. Make sure the script is executable (`chmod +x phpdocbrowser-installer`)
  4. Open a console and run the installer: ./phpdocbrowser-installer
  5. Answer the questions and you’r ready to go.

The script will create a directory, ./phpdocbrowser-installer, in your home directory in wich you will find ./phpdocbrowser-installer, ./phpdocbrowser-installer and a directory, ./phpdocbrowser-installer, with the PHP documentation. A “run script” will also be installed either in ./phpdocbrowser-installer or in your home directory depending on how you answered the questions and if you agreed to create a desktop shortcut one will hopefully appear on your desktop (havn’t tested this on KDE but it should work fine in Gnome).

To run the application hit the desktop shortcut, if one was created, or invoke ./phpdocbrowser-installer from a console.

If you wish to remove the PHPDoc Browser run the following from a console:
./phpdocbrowser-installer.

And that’s that!

Download

PHPDoc Browser 17:31, Sat 17 October 2009 :: 8.9 MB

Screen shots

PHPDoc

PHPDoc2

PHPDoc3

Syntaxer 2.0.2 released

This script is part of PLib

A few bugs was fixed in this release. I also noted a few new ones but that was mostly in the SyntaxMap class which gererates PHP arrays from the SyntaxMap files.

Changelog for this version

  • Fixed a bug where the end of strings (quotes) wasn’t found correctly in languages that has no escape character (like XSL, XML and HTML).
  • Also changed the Syntaxer::AutoDetect() method to return the extension of a file if no alias was found. In this way we can pass a path to the method and use the result directly as argument to `Sytaxer::__construct()`
4 lines of PHP
  1. $file = '/some/path/to/file.xsl';
  2. $lang = Syntaxer::AutoDetect($file);
  3. $stx = new Syntaxer($lang);
  4. $stx->Parse(file_get_contents($file));

The Syntaxer can be read about and downloaded over here.

Syntaxer 2.0.1 released

This script is part of PLib

I’ve released a new version of my generic syntax highlighting script Syntaxer. I fixed a potential bug where code generated on an operating system that only use \r to define a newline would be messed up (thank you jOOOL at PHPPortalen. Now I’ve bullet proofed (I hope) the way newlines are handled: I replace all \r\n with \r and then replace all \r with \n which means that we always end up with a single \n as the newline character.

The Syntaxer can be downloaded here

New syntax highlighting script

This script is part of PLib

A couple of years ago I wrote a generic syntax highlighting script. What I did was using the syntax files from Edit+ to determine how to parse a given language. All languages have different keywords, function names, delimiters and so on, and to know how to highlight a certain language you need to know these things. The Edit+ .stx files describes all these things.

Since I have gotten a few years more of knowledge, and PHP5 has arrived, I though I should write a new version of it. I could reuse some of the code but alot was rewritten and redesigned totally. The script has two classes:

  1. One class to parse the Edit+ syntax files which gets converted into PHP files so that the syntax files doesn’t have to to be parsed for every request. If the given .stx file has a newer timestamp than the cached PHP file the PHP file will be regenerated. Alot of this code could be reused from the older version
  2. The actual highlighting class. This class was almost entirely rewritten. Here I loop through every character of the code to highlight. When a keyword, delimiter or something else detectable is matched I grab that and searches forward to where the rule ends. In the older version I had a different approach where the code was splitted on newlines so I looped through line by line and for each line I looped throuh each charachter and did a similar match as in the new version.

    The new approach has some advantages:

    1. There’s no need to duplicate the code wich means a lot less memory is used.
    2. Fewer flags is needed since when I match a detectable rule I at once search for the end of the rule. This means that fewer .stx statements is needed wich speeds thing up alot.

      And foremost the code got much much cleaner!

Anyhow! There are a few minor bugs but the code is pretty usable (I have implemented it here in the blogging system). I added the scripts with documentation and a simple example on the server for anyone to download.

The Syntaxer2 can be found and downloaded over here. Some bug fixes and more examples will be done in the very near future.

Handy functions

How many times don’t you write code like this: $var = isset($_GET['var']) ? $_GET['var'] : 'default';? I read an article in Linux Format (the paper issue) about the up and comming PHP6 (well, it will probably take some time until it gets stable but you can already download it) and some of its new functions and stuff and learned that there will be a function called $var = isset($_GET['var']) ? $_GET['var'] : 'default';. This function will work like this:
$var = isset($_GET['var']) ? $_GET['var'] : 'default';. Pretty much nicer than the old way!

So I though: How hard can it be to implement the same kind of functionality in a user defined function. Well, it’s not hard att all, and here’s how simple it is:

22 lines of PHP
  1. /**
  2. * issetor
  3. *
  4. * @param mixed &$what Assoc array key
  5. * @param mixed $else Default value
  6. * @return mixed
  7. */
  8. function issetor(&$what, $else)
  9. {
  10. return isset($what) ? $what : $else;
  11. }
  12. #=================================================
  13. $arr = array('key1' => 'value1', 'key2' => 1);
  14. $var1 = issetor($arr['key1'], 'some value');
  15. echo $var1; # will echo: value1
  16. $var2 = issetor($arr['key5'], 'default for key 5');
  17. # will echo: default for key 5
  18. echo $var2;

The only thing noticable here is that the first argument needs to be passed by reference or PHP will throw a warning (depending on the error/warning level set).

Of cource you can escape this issue all together just by choking PHP errors/warnings – $var = isset($_GET['var']) ? $_GET['var'] : 'default'; – but that’s just pure ugly in my opinion.

Just by modifying the function above slightly we also get a nice way for writing:
$var = isset($_GET['var']) ? $_GET['var'] : 'default';

23 lines of PHP
  1. /**
  2. * isemptyor
  3. *
  4. * @param mixed &$what Assoc array key
  5. * @param mixed $else
  6. * @return mixed
  7. */
  8. function isemptyor(&$what, $else)
  9. {
  10. return empty($what) ? $else : $what;
  11. }
  12. #=================================================
  13. $var1 = "Hello";
  14. $var2 = "";
  15. $myvar = isemptyor($var1, 'Default');
  16. echo $myvar; # will echo: Hello
  17. $myvar = isemptyor($var2, 'Default');
  18. # will echo: Default
  19. echo $myvar;

And that’s it. I use them all the time!

Database abstraction layer

These classes is part of PLib

I can’t remember the last time I made a web site that wasn’t hooked up to a database in one way or another! There’s nothing wrong with using the database functions that PHP provide directly but if you work with different types of databases – MySQL, Postgres, Oracle, SQL Server, SQLite and so on – you need to remeber how the functions for these databases work, in what order to pass arguments and what not. And maybe you write an application that teoretically could use an abritrary database and not MySQL that you developed the application for, and then it would be nice to use the same function calls no matter what database is being used.

This is where the database abstraction layer comes in handy. There already exist many of them so, again and as always, why bother writing another one? Well, it’s fun and you always learn new stuff and, for good and bad, third party APIs tend to be packed with features that you never use so it get quite tedious learning the API.

So I wrote my own database abstraction layer that only contain the functionality I mostly use. If I need more functionaly it’s easy to implement. When I wrote the abstraction layer I tried to evolve my OOP (object orientated programming) skills (which isn’t too great) so that in it self was I goal.

The abstraction layer contains of two interfaces and two master classes: A connection interface and class and a result interface and class. To create a driver for a new type of database you inherit the master classes and implement the interfaces. This is how the skelleton looks like:

  • interface IDB
    The connection interface
  • interface IDBResult
    The query result interface
  • abstract class DB
    The connection master class
  • abstract class DBResult
    The query result master class

So a database driver should have two classes: One that extends the DB class and implements the IDB interface and one class that extends the DBResult class and implements the IDBResult interface.

Example

Here’s a simple example of usage:

41 lines of PHP
  1. <?php
  2. require_once 'Database.php';
  3. //! MySQL example
  4. try {
  5. $category = $_GET['category'];
  6. $db = DB::Create('MySQL', 'localhost', 'uname', 'pw0rd', 'mydatabase');
  7. $db->Connect();
  8. $res = $db->Query("SELECT * FROM table WHERE cat_id = '%d'", $category);
  9. if ($res->NumRows() > 0) {
  10. while ($row = $res->Fetch()) {
  11. echo "Title: " . $row->title . "<br/>";
  12. }
  13. }
  14. }
  15. //! Could be DBDriverNotFound, DBConnectionFail, DBNotFound, DBQueryFail
  16. catch (Exception $e) {
  17. die("Error: " . $e->getMessage();
  18. }
  19. //! SQLite example
  20. try {
  21. $category = $_GET['category'];
  22. $db = DB::Create('SQLite', 'mysqlitedb');
  23. $db->Connect();
  24. $res = $db->Query("SELECT * FROM table WHERE cat_id = '%d'", $category);
  25. if ($res->NumRows() > 0) {
  26. while ($row = $res->Fetch()) {
  27. echo "Title: " . $row->title . "<br/>";
  28. }
  29. }
  30. }
  31. //! Could be DBDriverNotFound, DBConnectionFail, DBNotFound, DBQueryFail
  32. catch (Exception $e) {
  33. die("Error: " . $e->getMessage();
  34. }
  35. ?>

As you can see here we’re using exactly the same code, except for the instantiation of the database object, wether we’re using MySQL or SQLite, and that’s the meaning of the database abstraction layer.

Sources

I currently have two implementations for MySQL and SQLite. The MySQL implementation is what I’m using for this blog and the SQLite implementation is not very well tested yet.

The classes are pretty well documented and the documentation is bundled in the package below.

Database abstraction layer 17:31, Sat 17 October 2009 :: 42.1 kB

Get mimetype for a file

Sometimes you need/want to find out what mimetype a particular file has. If you have access to the PEAR “Fileinfo Functions” you can use them, but if you don’t here’s a simple solution that might be helpful.

NOTE! This is not as reliable as the Fileinfo Functions but it works in most cases.

In most GNU/Linux systems (if not all) there’s a file called mime.types (in /etc/) which is mapping of mimetype -> extension. The file looks like this:

6 lines of Plain text
  1. application/x-gnumeric gnumeric
  2. application/x-go-sgf sgf
  3. application/x-graphing-calculator gcf
  4. application/x-gtar gtar tgz taz
  5. application/x-hdf hdf
  6. application/x-httpd-php phtml pht php

Quite obvious you could build an array from this file where the mimetype could be the key and the extension(s) the value as an array:

3 lines of PHP
  1. $_MIMETYPES = array(
  2. "mime/type" => array("ext1", "ext2", "ext3")
  3. );

When we’ve got this array populated we can easily look up a mimetype for an extention (and vice versa) by these two functions:

26 lines of PHP
  1. /**
  2. * Find mimetype for extension
  3. * @global array $_MIMETYPES
  4. * @param string $ext
  5. * @return string|bool
  6. */
  7. function find_mimetype($ext)
  8. {
  9. global $_MIMETYPES;
  10. foreach ($_MIMETYPES as $mime => $exts)
  11. if (in_array($ext, $exts))
  12. return $mime;
  13. return false;
  14. }
  15. /**
  16. * Get extension(s) for a given mimetype
  17. * @global array $_MIMETYPES
  18. * @param string $mimetype
  19. * @return array
  20. */
  21. function findextensionfor_mimetype($mimetype)
  22. {
  23. global $_MIMETYPES;
  24. return isset($MIMETYPES[$mimetype]) ? $MIMETYPES[$mimetype] : array();
  25. }

The script to convert the mime.types database into an array an auto generate the two functions above looks like this (note that this PHP script is intended to run as a command line (cli) script, thus the shebang (#!/usr/bin/php), since we don’t need to do this conversion run time but only once):

86 lines of PHP
  1. #!/usr/bin/php
  2. <?php
  3. /**
  4. * mimetyper.php
  5. * Turn the /etc/mime.types database into a PHP array
  6. * @author Pontus Ostlund <spam@poppa.se>
  7. */
  8. //! Mimetype file
  9. $mime_db = '/etc/mime.types';
  10. //! Resulting file for the PHP array
  11. $php_file = 'mimetypes.php';
  12. fileexists($mimedb) or die("No mimetype database ($mime_db) found\\n");
  13. $lines = @file($mimedb) or die("Couldn't read $mimedb");
  14. $fh = @fopen($phpfile, "w+") or die("Couldn't create $phpfile");
  15. $date = strftime("%A %B %d %Y", time());
  16. //! Write the PHP header to the file.
  17. @fwrite($fh,
  18. "<?php
  19. /
  20. * Find mimetype for file extension
  21. * Generated $date by " . basename(<strong>FILE</strong>) . "
  22. */
  23. $_MIMETYPES = array(n"
  24. );
  25. //! Loop through the lines array
  26. while ($line = array_shift($lines))
  27. {
  28. //! Match a comment or a blank line
  29. if ($line{0} == "#" || $line{0} == "\\n")
  30. continue;
  31. //! A line looks like:
  32. //! mime.type extention[ extension[ extension]]
  33. //! so lets grab the mimetype and extensions
  34. preg_match('/^(.?)\\s+(.?)$/', trim($line), $match);
  35. list(, $mime, $ext) = $match;
  36. if (empty($mime)) continue;
  37. //! This converts "ext ext ext" to "ext','ext','ext"
  38. $ext = join("', '", explode(' ', $ext));
  39. //! And lets write the new array index to the file
  40. fwrite($fh, sprintf(
  41. "\\t'%s' => array('%s')%s\\n",
  42. $mime, $ext, (empty($lines) ? "" : ","))
  43. );
  44. }
  45. //! Close the array, add a nifty search function, and close the PHP block
  46. //! and we're done!
  47. fwrite($fh, ");
  48. /**
  49. * Find mimetype for extension
  50. * @global array $_MIMETYPES
  51. * @param string $ext
  52. * @return string|bool
  53. */
  54. function find_mimetype($ext)
  55. {
  56. global $_MIMETYPES;
  57. foreach ($_MIMETYPES as $mime => $exts)
  58. if (in_array($ext, $exts))
  59. return $mime;
  60. return false;
  61. }
  62. /**
  63. * Get extension(s) for a given mimetype
  64. * @global array $_MIMETYPES
  65. * @param string $mimetype
  66. * @return array
  67. */
  68. function findextensionfor_mimetype($mimetype)
  69. {
  70. global $_MIMETYPES;
  71. return isset($MIMETYPES[$mimetype]) ? $MIMETYPES[$mimetype] : array();
  72. }
  73. ?>");
  74. fclose($fh);
  75. ?>

And this should result in a file called mimetypes.php that you just can include in you PHP scripts.

Download the scripts 17:31, Sat 17 October 2009 :: 4.5 kB