Rafael Sanches

August 5, 2009

reading java-style properties file in PHP

Filed under: caveats, php, programming — Tags: , , , , — mufumbo @ 4:46 am

It’s very strange that PHP only support the “parse_ini_string” as configuration function. I don’t like it at all! It has problems handling quotes, new lines, and other caveats.

The only benefit of parse_ini_string against Java Properties file is that it can handle “arrays”, but I don’t think that’s a benefit anyways. I had some trouble because I was wanting to use properties file in php for translations, since I only found buggy versions on the net I had build my own:

<?php
function parse_properties($txtProperties) {
 $result = array();

 $lines = split("\n", $txtProperties);
 $key = "";

 $isWaitingOtherLine = false;
 foreach($lines as $i=>$line) {

 if(empty($line) || (!$isWaitingOtherLine && strpos($line,"#") === 0)) continue;

 if(!$isWaitingOtherLine) {
 $key = substr($line,0,strpos($line,'='));
 $value = substr($line,strpos($line,'=') + 1, strlen($line));
 }
 else {
 $value .= $line;
 }

 /* Check if ends with single '\' */
 if(strrpos($value,"\\") === strlen($value)-strlen("\\")) {
 $value = substr($value, 0, strlen($value)-1)."\n";
 $isWaitingOtherLine = true;
 }
 else {
 $isWaitingOtherLine = false;
 }

 $result[$key] = $value;
 unset($lines[$i]);
 }

 return $result;
}
?>

This function can be used to create a php properties class. It should have the same behavior as the Java properties, so it should handle ” quotes and \ for new lines.

Sorry for the bad identation, wordpress hasn’t been very nice. Let me know if it have bugs 🙂

Advertisements

19 Comments »

  1. Hi Raphael,

    Is this source free for commercial and non commercial use?

    Regards
    Olivier

    Comment by Olivier — September 26, 2009 @ 12:12 pm

    • Hi Olivier. Yes, you can copy and paste it. I am glad that it helped someone out.

      Comment by mufumbo — September 27, 2009 @ 3:30 am

  2. Hi Raphael,
    Thank you for this function,I need it to read some Java .properties files
    I’m newbie with php ,I’ve got this error “Class ‘StrUtil’ not found in *****.php”
    Can u explain to me how to use exactly this function ?

    Regards
    Diaritus

    Comment by Diaritus — September 28, 2009 @ 10:32 am

    • hey Diaritus,

      I’ve updated the code on the page to don’t depend on that function.

      Anyway, the code of function endsWith in php is:
      function endsWith($Haystack, $Needle){
      if (is_array($Needle)) {
      foreach ($Needle as $n) {
      if (strrpos($Haystack, $n) === strlen($Haystack)-strlen($n)) {
      return true;
      }
      }
      }
      else if (strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle)) {
      return true;
      }

      return false;
      }

      thanks

      Comment by mufumbo — September 28, 2009 @ 6:27 pm

  3. it works!, Thanks Rafael, this saved my time!

    Comment by Gustavo from argentina — November 26, 2009 @ 4:47 pm

  4. Thx, this code helped me a lot!

    Comment by Monku — January 22, 2010 @ 8:13 am

  5. Works nicely, thanks a lot !

    Comment by Morgane — February 25, 2010 @ 3:30 am

  6. what about..?

    if (empty($line)) {
    continue;
    }

    if (preg_match(‘/([\w|\.]+)[ ]*= (.*)/’, $line, $matches)) {
    $key = $matches[1];
    $value = $matches[2];
    }

    $result[$key] = $value;

    also, i dont get the purpose of doing unset($lines[$i])

    Comment by emmanuel — May 4, 2010 @ 11:26 pm

    • hi emmanuel,

      With your matching you don’t parse properties files correctly, for example:

      key1=value1 is very cool
      key2=value2 is not very cool in your \
      code because you don’t parse the \
      new lines like the JAVA properties \
      files do, and if you put \
      an = here it will break everything.
      key3=value3
      #also=you will parse this line that should be ignored.

      I use strpos because it’s probably faster and more optimized than making a pattern matching on the line.

      The unset($lines[$i) serve to free the memory on the lines of the properties that were already processed. Imagine that you are processing a properties file of 100kb.. the memory is important.

      Also, my method is not optimized as it should.. it can be further optimized by not doing the split in the beginning and instead by iterating the string finding the “\n”

      thanks for the feedback
      rafa

      Comment by mufumbo — May 6, 2010 @ 3:57 am

  7. I changed

    $lines = split(“\n”, $txtProperties);

    to

    $lines = split(“\r\n”, $txtProperties);

    The carriage return was being read into the value. Not sure if it might just be better to trim the value.

    Comment by Minh — August 6, 2010 @ 11:06 pm

    • Actually, changed to:

      $lines = split(“\r\n|\n”, $txtProperties);

      Comment by Minh — August 6, 2010 @ 11:18 pm

  8. Hi,

    I found some problems in your implementation.

    Here are some definitions of .properties files:

    http://en.wikipedia.org/wiki/.properties
    http://download.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

    Problems:
    – Comment lines can begin with ‘!’ or ‘#’.
    – There can be any number of whitespaces before and after a key.
    – Every whitespace at the beginning of a value is ignored.
    – If a value goes thru more than one line it doesn’t add a linefeed at the end of each line. You can add linefeeds in the value with ‘\n’.
    – As delimiter between key and value are possible ‘:’, ‘=’ and ‘ ‘.
    – split is deprecated.
    – Possible ends of lines are ‘\n’, ‘\r\n’ and ‘\r’
    – It is better to use a resource handler to read the file then load the hole file into memory.

    In a few days I will have the time to write a new version. Maybe you can post it.

    Greetings

    Tom

    Comment by Tom — August 11, 2010 @ 6:14 pm

    • Hi Tom,

      That would be great! Send me the implementation once you have finished it.

      In my case my code is good enough, so i didn’t went further on details.

      My code handles “\\” for new lines.. you’re right about “\n”… in my case “\\” was enough..

      Thanks a lot
      Rafael

      Comment by mufumbo — August 17, 2010 @ 1:29 am

  9. For continued lines, you remove the backslash but reappend the newline. $value = substr($value,0,strlen($value)-1).”\n”;

    The newline should not be part of the continued value. The next line should be appended to the current value without either the backslash or the newline.

    Nice job otherwise!

    Comment by tex texin — January 30, 2011 @ 4:26 am

    • hey,

      I thought the new lines were done by “\n” string inside the text, and not by \ new line..

      is your comment correct 100%?

      thanks
      rafa

      Comment by mufumbo — January 30, 2011 @ 10:21 am

  10. Thanks for the function.

    Because you are using substr() to determine the value of a property, empty properties are set to FALSE which in my case is highly undesirable as I need to differentiate between uninitialized properties and FALSE properties.

    I prefer setting empty values to NULL:

    $result[$key] = empty($value) ? NULL : $value;

    Or some might prefer setting empty values to an empty string:

    $result[$key] = empty($value) ? NULL : $value;

    Comment by Marcus — March 17, 2011 @ 3:38 pm

    • oops the last line should read

      $result[$key] = empty($value) ? ” : $value;

      Comment by Marcus — March 17, 2011 @ 3:39 pm

  11. Great!

    I’ve found this one little to late, so I made my own to my own needs instead (I don’t need multiline statements, and I just wanted to be able to use the same .properties both in ant/java and php (for a builder script)).

    What this code is missing is the references you can have in java. For example:

    project.dir = /path/to/code/root
    builder.dir = ${project.dir}/builder

    So I’ve made a function to fix references, which takes a properties array which have references in the values as a string, and returns an array with references extracted, so it’s useful as a postprocessor for the result of the code above. But it’s only to my needs too, so it may have some differences between it and javas implementation.

    Feel free to use it as you like.

    function propertiesFixReferences( $properties ) {
    $keys = array_keys($properties);
    # don’t iterate over $properties; $properties going to change
    foreach( $keys as $key ) {
    $replacevalue = $properties[$key];

    foreach( $keys as $key2 ) {
    $properties[$key2] = str_replace( ‘${‘.$key.’}’, $replacevalue, $properties[$key2] );
    }
    }

    foreach( $properties as $key => $value ) {
    if( preg_match_all(‘/\$\{([^{}]*)\}/’, $value, $matches) ) {
    print “Undefined reference to ‘”.implode(“‘,'”,$matches[1]).”‘ in ‘$key’\n”;
    }
    }
    return $properties;
    }

    Can be found on: (or a newer version as it’s updated)

    https://github.com/pengi/chfm/blob/master/builder/propertiesParser.php

    Comment by Max Sikström — October 6, 2011 @ 8:19 am

    • hey max,

      that looks great. Feel free to use my implementation.

      I believe mine is a bit more optimized for performance since it only have one loop and your preg_match_all() is probably more expensive than mine split().

      I will have a deeper look once I have more time. Many thanks for submitting your solution.

      Comment by mufumbo — October 14, 2011 @ 2:46 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: