PHP Posts

Git

gitcreate

I’ve created a new repository on my GitHub account where I can commit some of the little scripts I’ve written for use on my server. The first one I’ve committed is gitcreate, a small script that automates the creation and bootstrapping of git repositories. I realized that, when I was creating a new repo on my server, I do the same things over and over. Create the repo, then add in some frameworks for whatever little thing I’m playing with at the time. Well, gitcreate can do all that for you. Create the repo and bootstrap in things like the most recent versions of CodeIgniter, jQuery, and Bootstrap. That way, when you clone the repo to start working, you’re already ready to start coding. Like most of my stuff, it’s licensed under the New BSD License.
Read More
PHP

PHP, methods, functions, and the global scope

It’s funny. Even after nearly 10 years with the language, there are still little gotchas that sometimes get me. I ran across one today. Say you have two objects, and the look like this: <?php class ObjA { public static function test() { global $test; var_dump($test); } } class ObjB { public static function test() { $test = 1; ObjA::test(); } } ObjB::test(); ?> It doesn’t work. You get NULL. Say I were to do something like this: <?php class ObjB { public static function test() { $test = 1; global $test; var_dump($test); } } ObjB::test(); ?> You also get NULL. And this: <?php function a() { $test = 1; b(); } function b() { global $test; var_dump($test); } a(); ?> Also fails. The reason is that the global scope on PHP is just that: global. Any time you’re in a function or method, you’re in a local scope and all local scopes are independent of each other. So you can’t global in something from one local scope to another. Variables are either global or local. That much I get and makes sense (and is in the documentation). What threw me for a loop was that PHP won’t copy something into the global scope from a local scope that is already defined **and will happily overwrite your local scope with a null value from the global scope if one doesn’t exist in the global scope, **in the process of creating the variable in the global scope. If you want a variable in the local scope to be global, you have to declare it as global before you write a value to it. Or, to put it another way: <?php class ObjA { public static function test() { global $test; var_dump($test); } } class ObjB { public static function test() { global $test; $test = 1; ObjA::test(); } } ObjB::test(); ?> Works beautifully.
Read More
Apple

Goodbye, Eclipse.

Dear Eclipse, We’ve known each other a long time, haven’t we? I remember when we first met. It was way back in 2005, two jobs ago when I was working at interactive Point of View. I was still a young, naive kid, just out of college. At the time I was just getting my start writing serious PHP code, and you were a breath of fresh air compared to what I had been using before (Dreamweaver). You seduced me with your awesome power and functionality. I used to love being able to have code on top and a browser window underneath. Ironically enough, one of my favorite features would eventually be something I couldn’t care the slightest about. Later that year I would move on to Asteria, and I took you with me. This was the first time I had two monitors on my desk, and I kept Eclipse in one, and a browser in the other while programming. Again, your raw power made complex tasks easy. I discovered Subversion integration, which made Tortoise (I was still on Windows at the time) irrelevant to me. Your Subversion tools turned me into a huge fan. When I moved jobs again, to dealnews, I again took you with me. Much to the chagrin of my coworkers, I preached the gospel of Eclipse. When I first started I was still in the Windows environment and my setup was much like it was at Asteria. Later that year when I switched to Mac, I again took you with me. You occupied a place of honor in my dock. We upgraded together. Through Callisto, Europa, Ganymede, Galileo, Helios and Indigo. We upgraded through Leopard, Snow Leopard and Lion together. Sure, we had our occasional disagreements and outright fights. I remember one time when you would absolutely choke on the size of dealnews’ code tree. I would try other editors and IDEs. I tried jEdit, Coda and TextMate. But I always came back to you. But all things change, and this time I think we’re finally through together. The first sign you were no longer interested in me was the dropping of the official PHP build - the one I had been using for years. But you knew I was worried - you even said so on your website and pointed me to PDT - PHP Development Tools. This aphrodisiac, you told me, would make our relationship just like we were kids again. But what you didn’t tell me was that PDT would make you crazy and unstable in the worst kind of way. Your behavior has become increasingly erratic whenever you take PDT. You developed bugs, including ones that I could no longer justify. Ones that were literally costing me time every day. You said PDT could auto-complete code and when it does it works great. But when it doesn’t, the display glitches up the file so badly that the only way to get back into a usable state is to close the file and reopen. Now imagine doing this four or five times for every file you’re editing, every time you try to auto-complete some HTML. Your ill tempered behavior is costing me time and money. I tried to talk with you about it, but all you could say was NullPointerException. So, I’ve thought a lot about this. It’s been a good six year run, but I think it’s time we ended our relationship together. The truth is that I know about your other boyfriend, too. I know his name is Android, and I know you guys have been spending a lot of time together. And I’m okay with it. Really. All things change and we all have to adapt. The truth is I’ve been fooling around some with your cousin Netbeans, and I think we’re really hitting it off. In many ways, she reminds me of you. The difference is, Netbeans has herself together, is trying hard to improve herself and hasn’t forgotten who her friends are, instead of getting strung out on PDT and spending all her time hanging out in the backseat of Android’s Pinto. So goodbye, Eclipse. What we had was wonderful while it lasted and I’ll always treasure our time together and the memories we made. I hope your new life works out. Maybe we’ll see each other from time to time, but I honestly I don’t think that would be fair to Netbeans. She’s my new IDE now. -Rob Peck Eclipse User, 2005-2011
Read More
Ramblings

RIP Dennis Ritchie

Unlike Steve Jobs, unless you’re in the tech industry, there’s a pretty fair chance you’ve never heard of Dennis Ritchie.
Read More
PHP

PHP Filtering: Validation, Sanitizing and Flags

PHP’s filter functions are really, really great. I’ve started using them almost any time I need to get input from a user and, personally, I don’t think you should use the old $_GET, $_POST unless you know what you are doing and there is some specific thing you’re trying to accomplish that you can’t with filter. Filter forces you to think carefully about what inputs your script takes and what format it takes them in. But there are also some behaviors of filter that can bite you in the rear if you aren’t really careful. One of these is knowing which flags you need to pass and what the difference between validation and sanitizing, when is the right time to  use each, and what flags to use. I ran into a good example of this today where I messed it up. I had configured filter_input_array to pull in a variable as FILTER_VALIDATE_FLOAT, probably because I wasn’t thinking like a user and instead was thinking like a developer. I’m the type of person that, when a form wants to know my phone number, I only enter 10 digits without parentheses or dashes. But users are different. They like friendly things. In this case, the user was entering “16,473.54” and the like into that box. Now, I can look at that and say, “yeah, that’s a float” (actually, it’s currency). It should be considered a valid value. But FILTER_VALIDATE_FLOAT will throw this out because it has a comma in it, unless you pass FILTER_FLAG_ALLOW_THOUSAND. Then, and only then, does it return the above as a valid value (in this case “16473.54”). But I looked at the code again. In this case, the value doesn’t need to be there except in a specific case, which I handled in error checking in the code, so I switched it to a Sanitize value instead. It’s probably a good idea to only use  FILTER_VALIDATE_* functions when your user has to give you a valid value and your script would fail if that wasn’t the case. If a validation returns false, you should fail the process and return a (nice) error message to the user. Sanitize functions allow you to accept a little wider range of data and still return a valid value from it. The docs have a great example of this involving email addresses. So if you’re writing PHP these days, definitely use filter. Just be careful and mind the flags and the difference between validation and sanitizing.
Read More
PHP

Interview Questions for Programmers

Over the years, I’ve seen a number of blog posts relating to common questions that should be asked of programmers. Obviously, this is going to depend on exactly what position you are hiring for, but there are some good “gateway” questions that can be used to determine whether or not an applicant you are interviewing can … well … even program at all. If they even have the mindset that makes a good developer. A common one I’ve seen tossed around is Fizz Buzz. The challenge goes something like this: Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”. Now, to anyone who even has a basic understanding of programming, this is super simple to solve using a modulus operator. But apparently many people applying for even entry-level development jobs cannot solve this problem. According to the article linked above, even one “senior developer” took 15 minutes to solve this problem. Earlier today, a friend posted something on Facebook that inspired what I think it another good, intermediate to difficult level programming question that also looks for pattern recognition. The relevant part of the post began by stating: “This year July has 5 Fridays 5 Saturdays and 5 Sundays.” There is the question! It would go something like this: The month of July 2011 has 5 Fridays, 5 Saturdays and 5 Sundays. Calculate the next 50 times there will be a month that has 5 Fridays, 5 Saturdays and 5 Sundays. Woah, so how to go about solving this problem? Well, look at a picture of July 2011. Notice something interesting about this month in relation to the question? This month has 31 days (the most any month can have), begins on a Friday and ends on a Sunday. And that’s the solution! It’s any month with 31 days that begins on a Friday! With this in mind, it’s pretty easy to come up with a PHP solution: <?php $count = 0; $num_found = array(); while(count($num_found) < 50) { $count++; $ts = strtotime("$count months"); if(date("t", $ts) == 31 && date("N", strtotime(date("Y-m-01", $ts))) == 5) { $num_found[] = date("F Y", $ts); } } print_r($num_found); ?> Note that I make use of PHP’s strtotime function, because it is the Swiss Army Knife of date manipulation in PHP. This would need to be adapted for use in another language. So now tell me: what are some other questions you’ve been asked or asked in an interview?
Read More
Apache

Automatically Provisioning Polycom Phones

The goal of this project were twofold: To completely eliminate the need for me to touch the phone to provision it. I want to be able to create a profile for it in the database, then simply plug the phone in and let it do the rest. And… To eliminate per-phone physical configuration files stored on the server. The configuration files should be generated on the fly when the phone requests them. So the flow of what happens is this: I create a profile for the phone in the database, then plug the phone in. Phone boots initially, receives server from DHCP option 66. Script on the server hands out the correct provisioning path for that model of phone. Reboots with new provisioning information. Phone boots with new provisioning information, begins downloading update SIP application and BootROM. Reboots. Phone boots again, connects to Asterisk. At this rate, provisioning a phone for a new employee is simply me entering the new extension and MAC address into an admin screen, and giving them the phone. It’s pretty neat. **Note: **there are some areas where this is intentionally vague, as I’ve tried to avoid revealing too much about our private corporate administrative structure. If something here doesn’t make sense or you’re curious, post a comment. I’ll answer as best I can. Creating the initial configs I used the standard download of firmware and configs from Polycom to seed a base directory. This directory, on my server, is /www/asterisk/prov/polycom_ipXXX, where XXX in the phone model. Right now we deploy the IP-330, IP-331 and IP-4000. While right now the IP-330 and IP-331 can use the same firmware and configs, since the IP-330 has been discontinued they will probably diverge sometime in the not too near future. With the base configs in place, this is where mod_rewrite comes into play. I added the following rewrite rules to the Apache configs: RewriteEngine on RewriteRule ^/000000000000\.cfg /index.php RewriteRule /prov/[^/]+/([^/]+)-phone\.cfg /provision.php?mac=$1 [L] RewriteRule /prov/polycom_[^/]+/[^/]+-directory\.xml /prov/polycom_directory.php` RewriteCond %{THE_REQUEST} ^PUT* RewriteRule /prov/[^/]+/([^/]+)\.log /prov/polycom_log.php?file=$1` To understand what these do, you will need to take apart the anatomy of a Polycom boot request. It requests the following files in this order: whichever bootrom.ld image it’s using, [mac-address].cfg if it exists or 000000000000.cfg otherwise, the sip.ld image, [mac-address]-phone.cfg, [mac-address]-web.cfg, and [mac-address]-directory.xml. So, we’re going to rewrite some of these requests to our scripts instead. Generating configs on the fly We’re going to skip the first rewrite rule (we’ll talk about that one in a little bit since it has to do with plug-in auto provisioning). The one we’re concerned with is the next one, which rewrites [mac-address]-phone.cfg requests to our provisioning script. So each request to that file is actually rewritten to provision.php?mac=[mac-address]. Now, in the database, we’re keeping track of what kind of phone it is (an IP-330, IP-331 or IP-4000), so when a request hits the script, we look up in the database what kind of phone we’re dealing with based on the MAC address, and use the variables from the database to fill in a template file containing exactly what that phone needs to configure itself. For example, the base template file for the IP-330 looks something like this: <sip> <userinfo> <server <?php foreach($phone as $key => $p) { ?> voIpProt.server.<?php echo $key+1 ?>.address="<?php echo $p["host"] ?>" voIpProt.server.<?php echo $key+1 ?>.expires="3600" voIpProt.server.<?php echo $key+1 ?>.transport="UDPOnly" <?php } ?> /> <reg <?php foreach($phone as $key => $p) { ?> reg.<?php echo $key+1 ?>.displayName="<?php echo $p["first_name"] ?> <?php echo $p["last_name"] ?>" reg.<?php echo $key+1 ?>.address="<?php echo $p["name"] ?>" reg.<?php echo $key+1 ?>.type="private" reg.<?php echo $key+1 ?>.auth.password="<?php echo $p["secret"] ?>" reg.<?php echo $key+1 ?>.auth.userId="<?php echo $p["name"] ?>" reg.<?php echo $key+1 ?>.label="<?php echo $p["first_name"] ?> <?php echo $p["last_name"] ?>" reg.<?php echo $key+1 ?>.server.1.register="1" reg.<?php echo $key+1 ?>.server.1.address="<?php echo $p["host"] ?>" reg.<?php echo $key+1 ?>.server.1.port="5060" reg.<?php echo $key+1 ?>.server.1.expires="3600" reg.<?php echo $key+1 ?>.server.1.transport="UDPOnly" <?php } ?> /> </userinfo> <tcpIpApp> <sntp tcpIpApp.sntp.address="pool.ntp.org" tcpIpApp.sntp.gmtOffset="<?php echo $tz ?>" /> </tcpIpApp> </sip> The script outputs this when the phone requests it. Voila. Magic configuration from the database. There’s a little bit more to it than this. A lot of the settings custom to the company and shared among the various phones are in a master dealnews.cfg file, and included with each phone (it was added to the 000000000000.cfg file). Now, on to the next rule. Generating the company directory Polycom phones support directories. There’s a way to get this to work with LDAP, but I haven’t tackled that yet. So, for now, we generate those dynamically as well when the phone requests any of its *-directory.xml files. This one’s pretty easy since 1) we don’t allow the endpoints to customize their directories (yet), and 2) because every phone has the same directory. So all of those requests go to a script that outputs the XML structure for the directory: <directory> <item_list> <?php if(!empty($extensions)) { foreach($extensions as $key => $ext) { ?> <item> <fn><?php echo $ext["first_name"]?></fn> <ln><?php echo $ext["last_name"]?></ln> <ct><?php echo $ext["mailbox"]?></ct> </item> <?php } ?> <? } ?> </item_list> </directory> We do this for both the 000000000000-directory.xml and the [mac-address]-directory.xml file because one is requested at initial boot (the 000000000000-directory.xml file is intended to be a “seed” directory), whereas subsequent requests are for the MAC address specific file. Getting the log files Polycoms log, and occasionally the logs are useful for debug purposes. The phones, by default, will try to upload these logs (using PUT requests if you’re provisioning via HTTP like we are). But having the phone fill up a directory full of logs is ungainly. Wouldn’t it be better to parse that into the database, where it can be easily queried? And because the log files have standardized names ([mac-address]-boot/app/flash.log), we know what phone they came from.Well, that’s what the last two rewrite lines do. We rewrite those PUT requests to a PHP script and parse the data off stdin, adding it to the database. A little warning about this. Even at low settings Polycom phones are chatty with their logs. You may want to have some kind of cleaning script to remove log entries over X days old. Passing the initial config via DHCP At this point, we have a working magic configuration. Phones, once configured, fetch dynamically-generated configuration files that are guaranteed to be as up-to-date as possible. Their directories are generated out of the same database, and log files are added back to the same database. It all works well! … except that it still requires me to touch the phone. I’m still required to punch into the keypad the provisioning directory to get it going. That sucks. But there’s a way around that too! By default, Polycom phones out of the box look for a provisioning server on DHCP option 66. If they don’t find this, they will proceed to boot the default profile thats ships with the phone. It’s worth noting that, if you don’t pass it in the form of a fully-qualified URL, it will default to TFTP. But you can pass any format you can add to the phone. if substring(hardware, 1, 3) = 00:04:f2 { option tftp-server-name "http://server.com"; } In this case, what we’ve done is look for a MAC address in Polycom’s space (00:04:f2) and pass it option 66 with our boot server. But, we’re passing the same thing no matter what kind of phone it is! How can we tell them apart, especially since, at this point, we don’t know the MAC address. The first rewrite rule handles part of this for us. When the phone receives the server from option 66 and requests 000000000000.cfg from the root directory, we instead forward it on to our index.php file, which handles the initial configuration. Our script looks at the HTTP_USER_AGENT, which tells us what kind of phone we’re dealing with (they’ll contain strings such as “SPIP_330”, “SPIP_331” or “SSIP_4000”). Using that, we selectively give it an initial configuration that tells it the RIGHT place to look. <?php ob_start(); if(stristr($_SERVER['HTTP_USER_AGENT'], "SPIP_330")) { include "devices/polycom_ip330_initial.php"; } if(stristr($_SERVER['HTTP_USER_AGENT'], "SPIP_331")) { include "devices/polycom_ip331_initial.php"; } if(stristr($_SERVER['HTTP_USER_AGENT'], "SSIP_4000")) { include "devices/polycom_ip4000_initial.php"; } $contents = ob_get_contents(); ob_end_clean(); echo $contents; ?> These files all contain a variation of my previous auto-provisioning configuration config, which tells it the proper directory to look in for phone-specific configuration. Now, all you do is plug the phone in, and everything else just happens. A phone admin’s dream. Keeping things up to date By default, the phones won’t check to see if there’s new config or updated firmware until you tell them to. But his also means that some things, especially directory changes, won’t get picked up with any regularity. A quick change to the configs makes it possible to schedule the phones to look for changes at a certain time: <provisioning prov.polling.enabled="1" prov.polling.mode="abs" prov.polling.period="86400" prov.polling.time="01:00" /> This causes the phones to look for new configs at 1AM each morning and do whatever they have to with them. Conclusions The reason all this is possible is because Polycom’s files are 1) easily manipulatable XML, as opposed to the binary configurations used by other manufacturers, and 2) distributed, so that you only need to actually send what you need set, and the phone can get the rest from the defaults. In practice this all works very well, and cut the time it used to take me to configure a phone from 5-10 minutes to about 30 seconds. Basically, as long as it takes me to get the phone off the shelf and punch the MAC address into the admin GUI I wrote. I don’t even need to take it out of the box!
Read More
PHP

Diffing, flattening and expanding multidimensional arrays in PHP

PHP has functions that can compute the difference between two arrays built in. The comments sections for those functions are filled with people trying to figure out the best way to do the same thing with multidimensional arrays, and almost all of them are recursive diffing functions that try to walk the tree and do a diff at each level. The problem with this approach are 1) they are unreliable as they usually don’t account for all data types at each level, and 2) they’re slow, due to multiple calls to array_diff at each level of the tree. A better approach, I think, is to flatten a multidimensional array into a single dimension, make a single call to array_diff, then (if needed) expand it back out if you really need the resulting diff to be multidimensional. Lets look at some code. The following recursive function flattens a multidimensional array into a single dimension. <?php function flatten($arr, $base = "", $divider_char = "/") { $ret = array(); if(is_array($arr)) { foreach($arr as $k = $v) { if(is_array($v)) { $tmp_array = flatten($v, $base.$k.$divider_char, $divider_char); $ret = array_merge($ret, $tmp_array); } else { $ret[$base.$k] = $v; } } } return $ret; } ?> The following function (based on this function found here) reinflates the array back up after it’s been deflated. <?php function inflate($arr, $divider_char = "/") { if(!is_array($arr)) { return false; } $split = '/' . preg_quote($divider_char, '/') . '/'; $ret = array(); foreach ($arr as $key = $val) { $parts = preg_split($split, $key, -1, PREG_SPLIT_NO_EMPTY); $leafpart = array_pop($parts); $parent = &$ret; foreach ($parts as $part) { if (!isset($parent[$part])) { $parent[$part] = array(); } elseif (!is_array($parent[$part])) { $parent[$part] = array(); } $parent = &$parent[$part]; } if (empty($parent[$leafpart])) { $parent[$leafpart] = $val; } } return $ret; } ?> Now, with arrays in flat form, it’s easy to use the built-in functions to diff: <?php $arr1_flat = array(); $arr2_flat = array(); $arr1_flat = flatten($arr1); $arr2_flat = flatten($arr2); $ret = array_diff_assoc($arr1_flat, $arr2_flat); $diff = inflate($ret); ?>
Read More
Apache

MySQL-based Apache HTTP Authentication for Trac and Subversion

In working on a side project with a few friendly developers, we decided to set up a Subversion repository and a Trac bug and issue tracker. Both of these, in normal setups, rely on HTTP authentication. So, being that we already had an authentication database as part of the project, my natural first thought was to find a way to authenticate Trac and Subversion of these against our existing MySQL authentication database rather than to rely on Apache passwd files that would have to be updated separately. Surprisingly, this was more difficult than it sounded. My first thought was to try mod_auth_mysql. However, from the front page, it looks as if this project has not been updated since 2005 and is likely not being actively maintained. Nonetheless, I gave it a shot and, surprisingly, got it mostly working against Apache 2.2.14. Notice I said “mostly.” It would authenticate about 50% of the time, while filling the Apache error logs with fun things like: [Sat Feb 13 11:11:27 2010] [error] [client -.-.-.-] MySQL ERROR: Lost connection to MySQL server at 'reading initial communication packet', system error: 0 [Sat Feb 13 11:11:28 2010] [notice] child pid 19074 exit signal Segmentation fault (11) [Sat Feb 13 11:34:14 2010] [error] [client -.-.-.-] MySQL ERROR: Lost connection to MySQL server during query: [Sat Feb 13 11:34:15 2010] [error] [client -.-.-.-] MySQL ERROR: MySQL server has gone away:` Rather than tear into this and try to figure out why a 5-year-old auth module isn’t working against far newer code, and with very little to actually go on, I just concluded that it wasn’t compatible and looked for a different solution. That’s when I came across mod_authnz_external. If your’e not familiar with this module, what it allows you to do is auth against a program or script running on your system, therefore allowing you to auth against anything you want - a script talking to a database, PAM system logins, LDAP, pretty much anything you have access to. All you have to do is write the glue code. In pipe mode, mod_authnz_external uses pwauth format, where it passes the username and password to stdin, each separated with a newline. It uses exit codes to return back to Apache whether or not the login was valid. Knowing that, it’s pretty easy to write a little script to intercept the username/password, run a query, and return the login. #!/usr/bin/php <?php` include "secure_prepend.php"; include "database.php"; $fp=fopen("php://stdin","r"); $username = stream_get_line($fp,1024,"\n"); $password = stream_get_line($fp,1024,"\n"); $sql = "select user_id from users where username='%s' and password='%s' and disabled=0"; $sql = sprintf($sql, $db->escape_string($username), $db->escape_string($password)); $user = $db->get_row($sql); if(!empty($user)) { exit(0); } exit(1); ?> Then, you just hook this into your Apache config for Trac or Subversion: AddExternalAuth auth /path/to/authenticator/script SetExternalAuthMethod auth pipe <Location /> DAV svn SVNPath /path/to/svn AuthName "SVN" AuthType Basic AuthBasicProvider external AuthExternal auth require valid-user </Location> Restart, and it should be all working. Some may argue that the true “right” way to do this is LDAP. But with just three of us, LDAP is overkill, especially when we already have the rest of the database stuf in place. The big advantage to this, even over mod_auth_mysql, is the amount of processing you can do on login. You basically can run any number of queries in your authenticator script - rather than just one. You can update with last login or last commit date, for instance. Or you can join tables for group checking; say you want someone to have access to Trac, but not Subversion. You can do that with this.
Read More
PHP

Drama? In My Developer Community?

… it’s more likely than you think! And here I thought drama was isolated to fandom mailing lists and MySpace! I was not at php tek this year. I keep meaning to make it to that conference, but, let’s face it, the week before Memorial Day is a really lousy time to have a conference. I usually like to take that Friday off to make it a long weekend. I may finally make tek next year, though. But, even if I went, I don’t usually get invited to the cool parties. It’s really for the best, though. I usually end up drunk in a bar listening to good music rather than trying to discuss functions and benchmarking after having imbibed a large quantity of booze or making an ass out of myself by diving into bushes. Ask me about that some other time. Apparently, at php tek, at one of these “cool-people-only” parties (okay, it was apparently an after-hours panel), a bunch of people cooked up this idea of having a uniform PHP coding standards amomg their own projects with the goal of having them adopted as some type of official standard. Now, in and of itself, this sounds like a good idea. Most other languages have at least a suggested best practices (Sun’s coding conventions for Java or Apple’s for Cocoa come to mind) even if you don’t use them. Every job I’ve worked in has had some standard, even if I had to write it. Most of them were derived from the PEAR standard, including what we do at dealnews. But hey, variety is the spice of life, right? What’s the harm in another choice? Nothing. So we’ve established that the idea of havng a[nother] PHP coding standard is not necessarily bad. The problem, as with all things, is what happened next… Somehow, they managed to get a closed mailing list on php.net. Think about that for just a second. This group, composed of some guys from some projects with no official relation to PHP other than being users of it, somehow ended up with [email protected]. WTF? I would love to know how that happened.More to the point, this will cause conceptual confusion among new, and even existing users. When I first heard about this, my first thought was, hey, this is on PHP.net, right? It must have some kind of official recognition, right? Well, as far as I can tell, it doesn’t. It’s just … some guys. Put yourself in the shoes of a new PHP user, visiting PHP.net for all your manual needs. Oh, what’s this? Standards? Well, I better use those! It was a suspiciously closed action for such an open-source project. The original mailing list was a closed list until Rasmus himself opened it, and the members don’t exactly seem keen on welcoming any input from anyone outside their little clique.Some of the things being said by the “PHP Standards Group,” quite frankly, make me very suspicious of their motives. Things like “All of us are too busy, both with real jobs and our various projects, to fight the battles that come of trying to make this a completely open process where anyone with an email address can contribute” reek of self-aggrandizing nonsense. I’m sorry, but that’s bullshit. Plain and simple. And the fact that no one else in the group has stood up to say otherwise speaks volumes. There’s a phenomenon that I have seen occur on mailing list called implicit acceptance. If you don’t stand up and say otherwise, you are implicitly agreeing with the stated course of action. So, if anyone in this group disagrees with the stated opinions, guys, now’s the time to man up. If you’re going to have a mailing list on php.net, and call yourselves the “PHP Standards Group,” you need to welcome input from the PHP community - all of us - not just your group. Otherwise, you don’t need to be on php.net, and you don’t need to be calling yourselves the “PHP Standards Group.” It is overly focused on OO. I know a lot of people think that objects are the answer to everything. I have strong disagreements, but I will save those for a later post. But (kind of tying into my previous point) there are a _lot _of people using PHP in a strictly functional way or in a way that sanely mixes functional and object oriented programming. Any standard - if it’s going to be called a PHP Standard - needs to take all widespread uses of PHP into accout, and not just OO. Now, as I said before, I’m not a “cool person.” I don’t have CVS commit access. I don’t have thousands of followers on Twitter or a cool blog (no offense to my five regular readers - you guys rule and I’ll buy you a round sometime!). I’m just some guy who’s been writing PHP for the last nine years or so. So, while it appears this “group” probably won’t care what I have to say anwyays, here is my humble suggestion for a path forward.** **Figure out the semantics. **Notice that all this stuff we’re talking is appearances and semantics. Nobody is discussing the actual proposals (as they have been made) so far, just the actions of the people involved. What exactly is this project trying to accomplish? Are you trying to write a standard for your project(s), or are you trying to produce something useful for the community? If this is just for your project(s), move it off php.net, call it something else (“The Shared Standards Working Group” or some other such nonsense), and do whatever the hell you want. But if you’re going to call yourselves the “PHP Standards Group,” and have your project on PHP.net, you have to welcome input from the community, even if you ultimately discard it. The thing I don’t understand is why this group appears so afraid of public input? Okay, the signal-to-noise ratio can get pretty high sometimes, sure. But for every ten, hundred or five hundred bogus suggestions you get, you may get one really good one. One you might not have thought of yourself or no one in your tight little circle might have seen. And this is the true power of any open-source project. I would urge the “PHP Standards Group” to overcome their fear of public input and let us - the users - have an input in the community process. As always, this represents my own views only, and not those of my employer, the beer I’m drinking (Fat Tire Amber) or my cat.
Read More