Send mail() using PHP on Mac OS X

Thanks to Benjamin Rojas, Andy Stratton, and a tip from Jasper, I was able to successfully send email from my home-brewed MAMP environment. Here’s the summary.

  1. Add the following to your /etc/postfix/sasl_passwd file:
    smtp.gmail.com:587 username@gmail.com:password

    (Of course, you don’t have to use GMail or port 587, but you get the idea.)

  2. Configure postfix:
    sudo postmap /etc/postfix/sasl_passwd
  3. Backup and edit your postfix configuration:
    sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
    sudo vim /etc/postfix/main.cf

    If you use TLS, then you will need to add the TLS settings but the other settings should already be there as a result of running the postmap command. You should have these options set in /etc/postfix/main.cf:

    mydomain_fallback = localhost
    mail_owner = _postfix
    setgid_group = _postdrop
    relayhost=smtp.gmail.com:587
    smtp_sasl_auth_enable=yes
    smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd
    smtp_sasl_security_options=
    smtp_use_tls=yes
    smtp_tls_security_level=encrypt
    tls_random_source=dev:/dev/urandom
  4. Start postfix:
    sudo postfix start

    If there are errors, you may need to edit your /etc/postfix/main.cf and restart postfix:

    sudo postfix reload
  5. Send a test message:
    date | mail -s test youremailaddress@yourdomain.com
  6. Make postfix start automatically on boot by opening your /System/Library/LaunchDaemons/org.postfix.master.plist file and adding:
    <key>RunAtLoad</key>
    <true/>

    Add this at the bottom just before the closing </dict> tag.

  7. Edit your /etc/php.ini file and configure the sendmail_path option:
    sendmail_path = "sendmail -t -i"

You should now be able to send email using PHP’s mail() function. If you continue to have issues, watch the contents of your postfix mail log:

tail -f /var/log/mail.log

Write tests, you might learn something

Writing unit tests for your code is widely regarded as a best practice. There are many excuses for not writing tests: time, cost, and the fun factor. Excuses aside, there are some Good Reasons to write tests for your code. I just discovered one today.

Write tests, you might learn something…faster

I’m working on an API integration that requires a bit of AES-256 encryption. Getting that worked out in PHP took some surprising turns, so just to be sure that I was getting all the details right (such as initialization vector size) I decided to write a unit test for my mcrypt wrapper library.

Since this library had worked well in other projects, imagine my surprise when the first AES-256 encrypt-decrypt test failed:

$ phpunit *
PHPUnit 3.7.8 by Sebastian Bergmann.

.F..

Time: 0 seconds, Memory: 2.50Mb

There was 1 failure:

1) EncryptionAes256Test::testEncryptDecrypt with data set #0 ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz')
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz'
+Binary String: 0x4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656668696a6b6c6d6e6f707172737475767778797a00000000000000000000000000

/Users/jhill/Sites/mrtz_010/Source/tests/EncryptionTest.php:53

FAILURES!
Tests: 4, Assertions: 7, Failures: 1.

Echoing out the decrypted string proved just as bizarre:

string(51) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz"
string(64) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz"

It seems that mcrypt pads the string with enough null characters (\0) to match the block size, and unlike strings in C, PHP strings do not necessarily end with the first null character.

The fix was obvious:

$decrypted = rtrim($decrypted, "\0");

Guess what? By investing time in a simple unit test, I unexpectedly learned something new about PHP, AND saved myself quite a bit of time debugging an API call that would not have worked.

I’d say it was worth it.

Scaling Your Website: Three Stages

So, you have A Big Idea that will Revolutionize The World. Naturally, people will flock to your site and engage with it. Naturally, you are concerned about whether your infrastructure can handle success. You wish to plan for success, it would be foolish not to, eh? Don’t let enthusiasm (or ignorance) lead you to premature over-optimization. After all, your Big Idea has not yet passed the test of time.

Here I will describe the three early stages commonly encountered when scaling a new website or startup application. Each one builds off the previous ones, starting with the lowest-hanging fruit and working up. Continue reading

What Is Wrong With PHP’s Semaphore Extension

Lack of a true Semaphore

sem_release() releases the semaphore if it is currently acquired by the calling process, otherwise a warning is generated.
http://php.net/manual/en/function.sem-release.php

By far the worst, this limitation makes a whole class of problems much more difficult to solve, and in fact means that PHP’s Semaphore extension, despite the name, is really a mutex (a semaphore with ownership, i.e., only the acquirer may release). Furthermore, it is inconsistent with the behavior of equivalent functions in other languages such as C and Python.

Please see phm\Lock\Semaphore for a kludgy workaround using mutexes, shared memory, and a message queue. Continue reading

Roll your own MAMP development environment

Pre-packaged MAMP, LAMP, and WAMP stacks have been common on developer’s computers for years. Such packages are convenient because they provide a single-step install process, with all components in the server stack preconfigured to work together, and off you go.

Except when they don’t.

I’ve learned from experience that these packages have ways of making you pay for the convenience you enjoyed up front. If you have ever needed to:

  • Install a PHP extension that wasn’t already provided in your stack
  • Run a specific version of PHP or MySQL
  • Install PEAR packages
  • Install SSL certificates
  • Run command-line PHP scripts

…you may have encountered some ugly, time-wasting surprises along the way.

It pays to know your environment inside and out. Today, it is quite easy to roll your own Apache-MySQL-PHP stack on Windows, Linux, or even Mac OS X. Continue reading

Box drawing in PHP

Back in the “good old days” of MS-DOS, you could draw lines, boxes, filled areas (think progress bars), and more using the extended ASCII character set (AKA code page 437).

While writing a simple command-line utility in PHP I wanted to use the full block () and light shade () characters to create a simple progress bar that is a bit nicer than the typical =========....................

Like this:

█████████████████░░░░░░░░░░░░░░░░░░░░░░░ 42.5%

Instinctively, I turned to PHP’s chr() function and looked up the extended ASCII codes for the characters I needed. Boy, was I disappointed when my progress bar was nothing but a series of un-useful question marks. Surely PHP can render simple ASCII characters, I thought.

It might have gone differently if I had been using a PC, but I do 100% of my development these days on a Macbook Pro. It so happens that the bash shell in UNIX, Linux, and Mac OS X all use UTF-8 encoding by default, not CP437. (Of course, your terminal font will need to support UTF-8 characters for this to work.)

So, I just needed to find the UTF-8 codes for the characters I needed and use those instead of the old familiar CP437 ones. However, the results weren’t much better.

After an hour or more of Googling and experimentation, I finally realized that chr() doesn’t do UTF-8. I found various suggestions on how to produce the desired characters using custom functions and the like, but the best way turned out to just use a good ‘ol HTML entity and run it through html_entity_decode:

$block = html_entity_decode('&#x2588;', ENT_NOQUOTES, 'UTF-8'); // full block
$shade = html_entity_decode('&#x2591;', ENT_NOQUOTES, 'UTF-8'); // light shade

Now, a progress bar isn’t exactly a box, so let’s demonstrate:

$tl = html_entity_decode('&#x2554;', ENT_NOQUOTES, 'UTF-8'); // top left corner
$tr = html_entity_decode('&#x2557;', ENT_NOQUOTES, 'UTF-8'); // top right corner
$bl = html_entity_decode('&#x255a;', ENT_NOQUOTES, 'UTF-8'); // bottom left corner
$br = html_entity_decode('&#x255d;', ENT_NOQUOTES, 'UTF-8'); // bottom right corner
$v = html_entity_decode('&#x2551;', ENT_NOQUOTES, 'UTF-8');  // vertical wall
$h = html_entity_decode('&#x2550;', ENT_NOQUOTES, 'UTF-8');  // horizontal wall

echo $tl . str_repeat($h, 15)  . $tr . "\n" .
     $v  . ' Hello, World! '   . $v  . "\n" .
     $bl . str_repeat($h, 15)  . $br . "\n";

Voilà!

UTF-8, once you get used to it, actually provides many more possibilities than CP437 did back in the MS-DOS days. Enjoy!