Blog
xdebug
I thought I'd written up an entry on
Xdebug for PHP a while ago, but apparently not. (Or, if I did, it's not coming up when I search for "xdebug" for some reason.) Anyway, Xdebug is a debugging tool for PHP. It's definitely worth enabling, if you're doing anything with PHP that's in any way non-trivial.
It's a bit hard to figure out the best way to get started with it under Windows. It turns out to be pretty easy, though, given a few pointers. First, go to the
wizard page, and paste in the contents of a phpinfo() call, run on the machine you're installing to. This will then tell you exactly which file to download, where to put it, and what to add to your php.ini. Much easier than trying to figure that stuff out yourself!
A few other pointers:
- Depending on how you download & copy the DLL over, you may need to unblock it. (Blocked files really annoy me, by the way. I don't know whose idea that "feature" was, but it was not a *good* idea. See
here for some ways to unblock multiple files easily.)
- Xdebug can add color to your var_dump() calls. Add "set html_errors = On" to your php.ini to enable this. I've found it to be very helpful when looking at a complex object. (Of course, there's always
krumo for that too.)
- To get debugging working under Komodo IDE, take a look at
this document. One thing that was confusing to me was that debugging locally is still considered "remote debugging".
Also, on a related note, I'm not sure I ever mentioned that, after looking at a few options, I settled on
Komodo as my PHP IDE of choice. It's reasonably fast and easy to use, and the debugger works pretty well. There are a few things about it that I'm not too fond of, but overall, it's really good.
Labels: PHP
Drupal Rules
I haven't blogged in a bit, and I have a few random items I want to write up. I'm doing a lot of work with Drupal, and I'm still learning some little tricks here and there.
When I'm writing custom code, I often use Drupal's cache. That's pretty easy to do, with
cache_set and
cache_get. I recently found myself in a situation, though, where I wanted to clear a certain item from cache any time a user created or edited a node of a certain type.
It turns out to be really easy to do that with the
Rules module. Just set up a rule that is triggered on insert or update of a certain node type, and make the action a simple line of PHP code:
cache_clear_all('your_cache_key', 'cache');
Easy!
Labels: drupal, PHP
Stumbling my way through the Drupal API
I've had to fix some interesting problems at work recently, related to a Drupal site that we'll be rolling out soon. I just finished fixing one issue that, while seemingly minor, took quite a while to figure out.
I'm really glad to have come up with a good solution. The thing that amuses me most about this is that, after more than eight hours of messing around, the final solution involved writing only about a half-dozen lines of code.
The problem, in a nutshell, is that we have a content type in the system that represents a university. There's a
location field on each node with, minimally, city and country specified. The user can search for locations, using some custom search code, and the search results are displayed via a standard Drupal
view. We allow the user to sort the results by one of a few different fields, with the sort drop-down exposed from the view. This works fine, except when sorting by country. On the location record, only the
two-letter country code is stored, for instance "AE" for "United Arab Emirates". So, when you sort by country, it's really sorting on country code, so "AE" goes to the top, which isn't really what the client wanted.
Of course, the first thing I did was Google the problem. I found
this issue discussion, which pretty much matches my problem. There was a suggestion in the comments there about using
hook_views_pre_render to re-sort the results right before displaying them. That works great, if you're not paging results. But, if you're pulling results back one page at a time from a large result set, this doesn't work, since the pre-render hook only gives you the current page.
So I figured out that I really need to sort by country name at the SQL level, while retrieving results. This led to my next problem, which is that, even with the location module installed, there's no SQL country lookup table in Drupal. The list of country codes and names is just stored in code, in an array, which can be retrieved via
_country_get_predefined_list. (You shouldn't call that directly, though, of course; you should use
country_get_list.)
So off I went to find a module that could give me a SQL table with country info in it. The
countries module does that, and a bit more. So, I installed that and figured out where the country table was. Then, my next blind alley was figuring out how to join to the new country table in a view. I was hoping I could just add a join to it in the view definition, and go from there. Well, I still don't know that much about Drupal views, and it didn't seem possible to do that easily.
So, the *next* blind alley was to see if I could alter the view SQL with
hook_views_query_alter, which seemed sensible. Well, the query object that you get from that hook isn't a nice simple query object that can easily be changed, so that turned out to be another dead end. (It's likely *possible* that I could have figured it out, but it seemed like the wrong approach.)
Then, finally, I stumbled across
this SO question. The one answer posted there led me in the direction of modifying the query with
hook_query_alter, which can be used to modify just about *any* query Drupal issues to MySQL. So, finally, I found a workable solution.
So that's it. I add a join, and replace the 'order by' clause. About a half-dozen lines of code. Oh, and I now also understand passing by reference in PHP a little better too!
Labels: drupal, PHP, programming
random PHP functions
I'm still doing a fair amount of PHP work. Right now, it's all Drupal, for a site we're rolling out very soon. I keep stumbling across random PHP functions I hadn't heard of before, and that turn out to be nice little time savers. Two examples:
- curl_setopt_array: I used to just call curl_setopt() a bunch of times to set all my options Now I can set a bunch of options in one fell swoop.
- http_build_query: Nothing I couldn't previously do with simple string concatenation, but this is much cleaner.
Labels: PHP, programming
PHP
I've been doing enough PHP work lately that
this blog post really speaks to me. Here's a good quote:
PHP isn't so much a language as a random collection of arbitrary stuff, a virtual explosion at the keyword and function factory.
I kind of like Drupal, which is of course written in PHP, but I think I'd like it more if it was written in... something else. I'm not sure what.
You can certainly write good things in PHP, and Drupal is an example of that.
CodeIgniter seems like a good thing too, though I haven't had much experience with it.
I recently had to do some work on what I'd call a "legacy" PHP site, which was basically all PHP spaghetti code, poorly done HTML, and questionable JavaScript. I wonder how many sites like that one are out there, written 5 or 10 years ago, possibly by an amateur, and working *just* well enough that nobody wants to pay to rewrite it from scratch. Probably a lot!
Labels: PHP, programming
Drupal 7 Development
I'm continuing my somewhat slow attempt to become a Drupal expert. After finishing up
a couple of general Drupal books from Packt, I started "Drupal 7 Module Development," also from Packt. I got up to chapter four, then put it down in frustration. I'll likely pick it up again, but it's not an easy book to read straight through, with little prior Drupal dev experience.
So, then I picked up "Pro Drupal 7 for Windows Developers," and I'm doing much better with that one. I just finished chapter 5, which walks you through the creation of a simple, but non-trivial, module. I found it fairly easy to follow, and a good start. The book is (obviously) written for Windows programmers looking to learn Drupal, specifically ASP.NET developers, so it's a good fit for me.
There's still a lot more to learn. Drupal's API and hook system are fairly complex and extensive. But I think I'm on the right path.
Labels: books, drupal, PHP
PHP IDEs
Since I'm doing more and more PHP development, I've been spending a bit of time trying to figure out if I can put together a decent development and debugging environment. Up until now, I've just been using
Notepad++ (on the PC) and
TextMate (on the Mac).
I've switched over to
Komodo Edit on both platforms now, and that works pretty well. To do debugging, you need to spend $300 on
Komodo IDE. But Komodo Edit does a lot, including auto-completion, syntax checking, and the ability to drill down into function definitions.
I like the idea of having a debugger, of course, so I decided to explore a couple of free IDEs that would support that.
Netbeans looks nice, but it's pretty heavy.
Eclipse PDT is a bit better, but still kind of bloated. I think I may have to spend the $300 on Komodo IDE.
Labels: drupal, PHP
Drupal 7 on Windows
I have a fairly reasonable "WIMP" stack (Windows, IIS, MySQL, PHP)
running on my work machine now, and I wanted to document how I got there,
for future reference, or for anyone who might stumble across this blog post. My setup has
evolved over time, so I'm not 100% sure if these instructions would work
exactly right from scratch.
There are some ways to get a Drupal-compatible "WAMP" stack
(Windows, Apache, MySQL, PHP) running on Windows pretty quickly, but
most of the stuff you really need to do in Drupal works OK in IIS. And I
think that the approach I outline below gives you the most flexibility
in terms of also using the components for stuff other than Drupal.
In these instructions, I'm creating a Drupal site named "drupal-7-test", but it doesn't really matter what you call it.
- Get Web Platform Installer from http://www.microsoft.com/web/platform/.
- Use Web Platform Installer to install "IIS 7 Recommended Configuration." You may also need to install URL Rewrite if you don't already have it.
- Install MySQL via the MySQL Installer for Windows. (Do not use Web PI.) Select "developer default" to get the MySQL Server, Workbench, and a few other useful things. For configuration, select "developer machine."
-
Install PHP 5.3.x from http://www.microsoft.com/web/platform/phponwindows.aspx with Web PI. (This will pull in a few other necessary components.) (Do not install the PHP for WebMatrix -- click the link on the above page.) (Oh, and the page still says "PHP 5.3.5," though the actual installer has been updated to a more recent version.)
- In IIS Manager, go to "default web site" and click "PHP
Manager". Make a note of the location of the config file and the error
log. Click "configure error reporting" and select "Development machine".
Click Apply.
- Get phpMyAdmin from http://www.phpmyadmin.net and unzip it to c:\inetpub\wwwroot\phpmyadmin. Go to http://localhost/phpmyadmin and run through the setup.
- In phpMyAdmin, create a user named "drupal_7_test". Click the
checkbox to also create a database of the same name, and give that user
all rights to it.
- Get Drupal 7 from http://drupal.org/download and unzip it to c:\inetpub\wwwroot\drupal-7-test. (The standard Drupal 7 download includes a web.config that enables clean URLs for IIS, given the MS IIS Rewrite module.)
- Give IIS_IUSRS modify rights to sites/default/files.
- Go to http://localhost/drupal-7-test and run through the setup. Use the MySQL database & user created earlier.
- Go to the status report in the Drupal admin and check that everything is working OK.
Random follow-up notes
It's June 10, and I'm setting up a WIMP environment on my ThinkPad. I've also recently set up a WIMP environment on my new work desktop, so I've got a few follow-up notes on this post.
- You may need to enable CGI on your machine, if it's not already enabled, to get PHP working. See here for details.
- As an alternative to the full MySQL installer I linked to above, you can also use the stripped-down version that can be found here.
Labels: drupal, PHP, Windows
1&1 Linux
In anticipation of installing Drupal on my 1&1 account soon, I went into my control panel and poked around a bit. First, I found that my account was set to use PHP 4. It was pretty easy to switch it to PHP 5. A call to phpinfo() shows that I'm now at 5.2.17. That's not quite up to date, but it's probably close enough.
I also looked into the MySQL setup. Several years ago, I set up a MySQL database on my account. That database is still there, at MySQL 4, with a 100 MB limit. Just for the heck of it, I created a new database. The new one is MySQL 5, and has a 1 GB limit. So, that's nice. (There doesn't seem to be any way to upgrade the old MySQL 4 db to MySQL 5, but that's fine, since it's empty.)
I even went as far as uploading the Drupal 7 tar.gz file today, but the 1&1 web file browser can't unzip tar.gz files, so I'm going to need to get to a command prompt to do that, and it's a little late to get into that tonight.
Labels: Linux, PHP
Drupal 7
After staying out too late Tuesday night, then going to bed at 8:30pm last night, I finally had some free time after work tonight to play around with Drupal. I installed Drupal 7 on my MacBook, following
these instructions. The only real trouble I had was in making sure that every host reference was set to 127.0.0.1 rather than localhost, or anything else.
Most of the obvious stuff seems to be working. I haven't figured out
clean URLs yet though.
Labels: drupal, PHP, programming
xAMP on the Mac
I've been trying to learn a bit about
Drupal recently. It looks like we might be getting some Drupal projects at work, so it seemed like a good idea.
To get a working setup for Drupal on my Mac, I wanted to get all the pieces in place -- Apache, PHP, and MySQL, basically. Apache is, of course, already there, and I already had that turned on, so no problem.
PHP was already installed, but apparently got turned off during the 10.7 upgrade. All you need to do to turn it on is edit httpd.conf, and uncomment one line, per
this SO page.
For MySQL, there's an installer that works pretty smoothly, per
this page. One odd thing I stumbled across at one point is that you usually need to refer to your local server as '127.0.0.1' rather than 'localhost'. Long story, but something worth noting. Also, if you're not sure how to set the root password, take a look at
this SO page.
I tested to make sure that MySQL was working from PHP using this litle test script:
<?php
$db = mysql_connect("127.0.0.1:3306", "root", "password");
if (!$db) {
die('Could not connect' . mysql_error());
}
echo 'Connected successfully';
?>
I can't remember exactly where I found that, but it's a pretty basic script.
I then got a little ambitious and decided to try to get
phpMyAdmin working. I made a couple of simple mistakes here, including not quite understanding that config.inc.php needed to be in the root phpMyAdmin folder and not in the config subfolder.
Also, the warning from phpMyAdmin about
mcrypt not being installed was bugging me, so I decided to try and fix that. That turned out to be kind of complicated. I followed
these instructions, and they worked, but only on my second try. I must have gotten something wrong on the first try. Also, I found
another page with similar instructions, so referencing that may help if anything on the first page seems confusing.
In the end, I think I really should have just gone with
MAMP, but of course I was doing this as a learning exercise, so it was valuable to go through all this, even if it took a lot longer than was probably necessary.
And I still don't have Drupal installed. Maybe tomorrow!
Labels: Apple, drupal, PHP, programming
PHP tip of the day
If you're running an array through json_encode(), and part of that array is a reference to another array, do _not_ json_encode() *that* array first. JSON-encoding something that's already JSON results in... confusion. This is one of those things that I would have figured out a lot faster if I had any kind of debugging set up for PHP. Maybe I should look at
FirePHP. I don't do a lot of PHP work, but I'm using PHP right now to test some REST web services that I'm writing in .Net/C#. (Debugging the .Net code, of course, is a piece of cake.)
Labels: PHP, programming
Blogger label cloud
I just found a PHP script by
this guy that does pretty much the same thing that my script does, except in the form of a cloud instead of a list. Nice. He also uses a cache file so he doesn't have to read the directory on every call. Both of those were things that I had thought about adding to my PHP program. Now I can probably just borrow this guy's code.
Labels: Blogger, PHP
more fun with Blogger tags and PHP
I wanted to enhance my tag list PHP program to do a couple more things. Basically, I wanted it to be aware of when we are on a label page, so that we could make the list item for the current page plain text (instead of a link), and so that I could put a link back to the main page on any label page.
Normally, I have a link back to the main page in the header of any page other than the main one. I do this through the Blogger template, using the "ItemPage" and "ArchivePage" Blogger template tags to figure out when we're not on the main page. Unfortunately, there's no "LabelPage" template tag, as far as I can tell.
Making one of the list items text rather than a link broke the sorting, since I was just sorting on the text of the list item, including the "a href..." stuff. To get around this, I switched to using an associative array, where the key value is the tag.
Here's the code:
<?php
$dirname = "."; # current directory
$uri = $_SERVER["REQUEST_URI"]; # the page we're on
$bOnLabelPage = false; # are we on a label page?
$dir = opendir($dirname);
$file_list = array();
$i = 0;
while (false != ($file = readdir($dir)))
{
if (ereg(".html$", $file))
{
$tag = substr($file, 0, strlen($file)-5);
$key = strtoupper($tag);
if (ereg("$file$", $uri))
{
$file_list[$key] = "<li>$tag";
$bOnLabelPage = true;
}
else
$file_list[$key] = "<li><a href='/labels/$file'>$tag</a>";
$i++;
}
}
closedir($dir);
#natcasesort($file_list);
ksort($file_list);
?>
<h1>My Tags</h1>
<?php
if ($bOnLabelPage) echo ("<a href='/'>[Back to main page]</a>");
?>
<ul>
<?php foreach ($file_list as $file) echo($file); ?>
</ul>
A little more complex than what I started with, but still not too bad.
As a side note, I am using
TextWrangler to edit PHP files on my Mac, and it's working pretty well. It's got syntax-highlighting for PHP and HTML. I'm also now using
Fugu to copy files up to my server. It integrates well with TextWrangler, so that I can just keep a file open in TextWrangler and have it copied back to the server every time I save. Nice.
On Windows, I'm mostly using
Multi-Edit and
WinSCP. That combo works pretty well too, though I'm using an older version of Multi-Edit that doesn't have PHP syntax highlighting.
Labels: Blogger, PHP
sorted list of tags
OK, this time I've revised my PHP code to show a sorted list of labels:
<?php
$dirname = "."; # current directory
$dir = opendir($dirname);
$file_list = array();
$i = 0;
while (false != ($file = readdir($dir)))
{
if (ereg(".html$", $file))
{
$tag = substr($file, 0, strlen($file)-5);
$file_list[$i] = "<li><a href='/labels/$file'>$tag</a>";
$i++;
}
}
closedir($dir);
natcasesort($file_list);
?>
<h1>My Tags</h1>
<ul>
<?php foreach ($file_list as $file) echo($file); ?>
</ul>
Not a big deal, but it does show a (probably) typical use of arrays in PHP. As a side note, the "foreach" construct in PHP is a bit different from the usual foreach in other languages. It's usually something like "foreach (item in list)", while in PHP it's reversed: "foreach (list as item)". I should also mention that I first tried the regular sort() function, but switched to natcasesort(), since a case-insensitive sort makes more sense here.
Labels: Blogger, PHP
© 2011 Andrew Huey