Webdev tech notes


return to topUpgrading to PHP 7

[OS 10.10.5 Yosemite]

Upgrading to PHP 7 on macOS 10.10 (Yosemite) did not go smoothly. The installation script from LIIP almost worked, but failed in its final steps. But it's not hard to patch things up. Here's what worked for me (kudos to Neil Gee's Coolest Guides on the Planet and several of his readers' comments).

The LIIP installation script yielded this:

% curl -s https://php-osx.liip.ch/install.sh | bash -s 7.0 Detected OS X Yosemite 10.10. All ok. Get packager.tgz Unpack packager.tgz Please type in your password, as we want to install this into /usr/local Password: Start packager (may take some time) downloading https://s3-eu-west-1.amazonaws.com/php-osx.liip.ch/install/7.0-10.10-... downloading https://s3-eu-west-1.amazonaws.com/php-osx.liip.ch/install/7.0-10.10/... Installing package 7.0-10.10-frontenddev into root / ./pkg/pre-install pkg/pre-install Skipping existing directory Skipping existing directory usr/ Skipping existing directory usr/local/ Extracting usr/local/php5-7.0.11-20160923-203451/ ... Extracting usr/local/php5-7.0.11-20160923-203451/bin/xsltproc Executing post-install script /tmp/7.0-10.10-frontenddev-post-install rm: /usr/local/php5: is a directory cp: /usr/local/php5/lib/php.ini-development: No such file or directory Create symlink /usr/local/php5/entropy-php.conf /etc/apache2/other/+php-osx.conf Restarting Apache httpd: Syntax error on line 537 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+php-osx.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): Symbol not found: _environ\n Referenced from: /usr/local/php5/libphp5.so\n Expected in: /usr/sbin/httpd\n %

This results in a broken httpd install. To confirm that it's broken:

% apachectl configtest httpd: Syntax error on line 537 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+php-osx.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): Symbol not found: _environ\n Referenced from: /usr/local/php5/libphp5.so\n Expected in: /usr/sbin/httpd\n %

To fix it:

  1. Save the old PHP directory and create a symlink to the new one

    % cd /usr/local % sudo mv php5 php5-myoldversion % sudo ln -s php5-7.0.11-20160923-203451 php5 %

  2. Edit php5/entropy-php.conf and change this line:

    LoadModule php5_module /usr/local/php5/libphp5.so

    to this:

    LoadModule php7_module /usr/local/php5/libphp7.so

  3. To verify that it's working:

    % apachectl configtest Syntax OK %

  4. Restart Apache:

    % sudo apachectl graceful %

  5. Finally, to use the correct PHP command line interface, edit your .bashrc or .cshrc, etc., to make sure that the $PATH environment variable gives /usr/local/php5/bin precedence over /usr/local/bin/. This ensures that you'll be using the right one:

    % which php /usr/local/php5/bin/php % php -v PHP 7.0.11 (cli) (built: Sep 23 2016 20:33:19) ( NTS ) Copyright (c) 1997-2016 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies with Zend OPcache v7.0.11, Copyright (c) 1999-2016, by Zend Technologies with Xdebug v2.4.1, Copyright (c) 2002-2016, by Derick Rethans %

Welcome to the brave new world of PHP 7.

return to top Sending email from PHP scripts

[OS 10.10.2 Yosemite]

Sending email from PHP forms can be tricky. PHP's built-in mail() function works OK if the webhost (a) has sendmail set up correctly and (b) it isn't frequently blacklisted by SpamHaus (booo, DreamHost!). But even then, it can be a tad cumbersome to set up all of mail()'s arguments. The simplest and most portable solution is the class PHPMailer.

Setting up PHPMailer is a breeze ‐ especially when using a SMTP server such as Gmail's. Here's how:

  1. Download PHPMailer.
  2. Install these three files into your site: class.phpmailer.php, class.smtp.php, and class.pop3.php .
  3. The following sample code, adapted from Felipe Alameda A's Stackoverflow answer, works for me.

    include_once("class.phpmailer.php"); include_once("class.smtp.php"); include_once("class.pop3.php"); $mail = new PHPMailer(); $mail->IsSMTP(); // Use SMTP $mail->Host = "smtp.gmail.com"; // Sets SMTP server $mail->SMTPDebug = 2; // 2 to enable SMTP debug information ( delete this line when it's running ok) $mail->SMTPAuth = TRUE; // enable SMTP authentication $mail->SMTPSecure = "tls"; //Secure conection $mail->Port = 587; // set the SMTP port $mail->Username = 'USERNAME@gmail.com'; // SMTP account username $mail->Password = 'PASSWORD'; // SMTP account password $mail->Priority = 3; // Email priority (1 = High, 3 = Normal, 5 = low) $mail->CharSet = 'UTF-8'; $mail->Encoding = '8bit'; $mail->ContentType = 'text/html; charset=utf-8\r\n'; $mail->From = 'YOU@YOURDOMAIN.COM'; $mail->FromName = 'GMail Test'; $mail->WordWrap = 900; // RFC 2822 Compliant for Max 998 characters per line $mail->AddAddress("SOMEONE@SOMEDOMAIN.COM"); // To: $mail->isHTML( TRUE ); $mail->Subject = 'Test Email Using Gmail'; $mail->Body = $MessageHTML; $mail->AltBody = $MessageTEXT; $mail->Send(); $mail->SmtpClose();

PHPMailer's error messages (generated when you set SMTPDebug) are very helpful. Read them carefully.

See also this tech note: "Send email from command line via Gmail SMTP in Mac OS X Yosemite"

return to top Notes on ensuring that your HTML is also valid XML

Some tips collected from around the Internet:

Entities that are predefined in XML:

EntityRendered as

Entities that must be converted to their numerical form:

EntityRendered asNumeric character reference
&copy; © &#x00A9;
&mdash; &#x2014;
&sect; § &#x00A7;
&rarr; &#x21D2;

See Wikipedia's List of XML and HTML character entity references.

return to top Notes on migrating from HTML4 to HTML5

Some tips collected from around the Internet:

  1. Move <table> attributes cellspacing, cellpadding and <td> attributes align, valign to CSS:
    // cellpadding th, td { padding: 5px;} // cellspacing table { border-collapse: separate; border-spacing: 5px; } // cellspacing="5" table { border-collapse: collapse; border-spacing: 0; } // cellspacing="0" // valign th, td { vertical-align: top; } // align (center) table { margin: 0 auto; }

    solution provided by user "drudge" at StackOverflow.com

  2. In <script> elements, eliminate type='text/javascript' and language='javascript' attributes.
  3. In <img /> elements, move align='...' to CSS.

return to top Quickie: setting up mySQL & PhpMyAdmin

Note: For a much simpler, clearer, better tutorial on all this, please see Jeff Moore's article "Working with PHP 5 in Mac OS X 10.5 (Leopard)".

These notes are based on my experience installing and configuring mySQL and phpMyAdmin on my Mac (Dual PowerPC G5 / OS X 10.4.11). Maybe they’ll also be helpful to you. They are no substitute for the "official" installation instructions. They are strictly "as-is". Use at your own risk. Mileage may vary, etc.

  1. Make sure your local web server is turned on. (Open the "Sharing" panel in your System Preferences, and make sure the "Personal Web Sharing" item is checked. Note: this is where you can also change your computer’s name.)
  2. Point your browser to http://localhost . If you haven’t already configured a local website, you’ll get the default Apache web page (~/Sites/index.html).
Installing mySQL
  1. Get the latest version.
  2. Double-click the .dmg file to mount the mySQL disk image
  3. There are two mySQL packages in the disk image file. Double-click the one named mysql-foo.pkg, where foo contains this mySQL version number and your OS.
  4. Follow the installer instructions. This installs mySQL in /usr/local/mysql
  5. Add /usr/local/mysql/bin to your shell’s search path (if you don’t know how to do this, see the ReadMe.txt file).
  6. In Terminal:

    % sudo /usr/local/mysql/bin/mysqld_safe # (enter password, if asked) % ^Z # suspend the daemon % bg # resume daemon as a background task

  7. Create mysql’s "root" user. In Terminal:

    % sudo mysqladmin -u root password somePassword

    Here "somePassword" is a password for mysql’s "root" user. This "root" is not the same as the system’s "root" user. Go figure. Read Apple’s docs for more details.

  8. Test the installation. In Terminal:

    % mysql -u root somePassword # test mysql

    This should spit out a list of mysql options and variable settings. If not, you’ll have to find some more in-depth docs. (Start with the ReadMe.txt.) Sorry.

Installing PhpMyAdmin
  1. Get the latest version.
  2. Unzip the file into ~/Sites .
  3. This creates a directory with a long and hard-to-remember name. Rename it phpMyAdmin.
  4. In Terminal:

    % cd ~/Sites/phpMyAdmin % mkdir config # create directory for saving % chmod o+rw config # give it world writable permissions

  5. Point your browser to http://localhost/~YOURUSERNAME/phpMyAdmin
  6. You should see a "Welcome to phpMyAdmin" page. It will probably contain an "Access denied" error message because you haven’t created a config file. Click on the link to the "setup script".
  7. In the "Servers" row, click the "Add" button. To avoid having to enter a password each time you run phpMyAdmin, enter the mySQL "root" password in the "Password for config auth" field. You can accept all the other defaults. Click the green "Add" button.
  8. Click the "Save" button in the "Configuration" row.
  9. In terminal:

    % cd ~/Sites/phpMyAdmin % mv config/config.inc.php .

    The phpMyAdmin docs go further, and ask you to do this:

    % chmod o-rw config.inc.php # remove world read and write permissions

    But when I do that, phpMyAdmin is denied access. Since I’m hiding behind a firewall I’m not going to worry about those permissions right now....

If you point your browser to http://localhost/~YOURUSERNAME/phpMyAdmin you might get this error message:

MySQL said: #2002 - The server is not responding (or the local MySQL server’s socket is not correctly configured)

If so, do the following (courtesy of Ben VanScoter):

% sudo mkdir /var/mysql % sudo ln /tmp/mysql.sock /var/mysql/mysql.sock % sudo apachectl graceful /usr/sbin/apachectl graceful: httpd gracefully restarted %

Works for me.

Now you’ll be able to create and manage mySQL databases using phpMyAdmin, to your heart’s content.

return to topQuickie: using svn for website development

After years of dithering, I finally made the switch to using version control to manage my websites. Looking back, I can only wonder what took me so long. Now I use Subversion. I am happy. Why? Here are some reasons:

  • It provides a seamless way for several people to collaborate on the same project without stepping on each other’s toes. (Have you ever had a collaborator delete one of your crucial files?)
  • You can easily document the changes you make to your code.
  • If you make a mistake, You can easily "roll back" to an earlier version.

Making the switch to Subversion is well worth the modest effort required. (If I can do it, so can you.) This "Quickie" article will take you through (1) creating an online repository for your site; (2) creating a development site for doing live testing on your live server; and (3) using Subversion in routine development work. There are, doubtless, other, better, ways to accomplish all this, but this is what has worked for me.

Assumptions in this article

  • You have a basic grasp of what Subversion is, what it’s for, and (roughly) how to use it
  • Subversion is installed on your server and on your local computers
  • You have a site (we’ll call it www.example.com) that’s already up and running on your Apache server. I’ll also assume your site lives in the server directory ~/example.com (i.e., in your home directory)
  • You have login (shell) access to the server


For starters, you’ll need to create two new sub-domains. Follow your web host’s instructions about how to do that:

  1. Create two new sub-domains: svn.example.com (for your svn repository) and dev.example.com (for your development site). [If you’re using DreamHost, go to your webpanel and select Domains » Manage Domains » Add New Domain/Sub-Domain.] It’s a good idea to password-protect your dev site, so that only you and your co-developers have access to your not-quite-ready-for-prime-time version of the site.
  2. Create a new Subversion project. We’ll give it the id "myproject", and install it to the svn.example.com URL. (Don’t forget the username and password you choose here!) [If you’re using DreamHost, you can create a new Subversion project right from your webpanel: select Goodies » Subversion.]

The rest of the setup happens through the login shell on your server. So log on to your server and do the following:

  1. To hide the Subversion directories from web browsers, create (or edit) the .htaccess file in ~/dev.example.com. Add the following line:

    RedirectMatch 403 /\\.svn.*$

  2. Add this line also to the .htaccess file in ~/www.example.com.
  3. Create an empty trunk/branch/tag directory structure as the initial import into the project. (In the following, "YOURNAME" is your svn username.)

    % cd % mkdir tmp % cd tmp % mkdir branches trunk tags % svn import . --username YOURNAME -m "initial import" http://svn.example.com/myproject Authentication realm: <http://svn.example.com:80> My Project Password for ’YOURNAME’: {enter password} Adding trunk Adding branches Adding tags Committed revision 1. % cd .. % /bin/rm -fr tmp

  4. Now go ahead and import your current site (www.example.com) into the repository:

    % cd ~/www.example.com % svn import . --username YOURNAME -m "importing website" http://svn.example.com/myproject/trunk

    If your site contains some dirs that you want to exclude from the development version (e.g., wordPress), you might prefer to import the site on a directory-by-directory basis:

    % cd ~/www.example.com % svn import ./keep_me1 --username YOURNAME \\ -m "importing keep_me1 directory" http://svn.example.com/myproject/trunk/keep_me1 % svn import ./keep_me2 --username YOURNAME \\ -m "importing keep_me2 directory" http://svn.example.com/myproject/trunk/keep_me2

    Or, to be more efficient about it, you could run a little shell script like this:

    #!/bin/tcsh -f foreach d ( some_dir another_dir yet_another_dir ) { svn import $d --username YOURNAME -m "importing $d directory" http://svn.example.com/myproject/trunk/${d} }

  5. Checkout a working copy to the development site (dev.example.com):

    % svn co http://svn.example.com/myproject/trunk ~/dev.example.com

Your dev site (dev.example.com) now contains a full working copy of the latest revision of your site.

Routine workflow

  1. Checkout a working copy from the repository to your local machine. (I recommend the svnX GUI client for the Mac.)
  2. Develop and test your code, etc., on your local machine.
  3. At logical stages (say, when you fix a bug or finish writing a new function), commit your changes to the repository. Some good habits to practice: (1) Always update your working copy before each commit. Always jot down a meaningful comment with each commit. (Instead of saying "Fixed a bug", say "Fixed function Foo() in bar.php that had wrong mySQL query.")
  4. Log on to your server and update the development site:

    % svn up ~/dev.example.com

  5. View the dev site. You may discover bugs on the dev site that weren’t apparent on your local machine. (They might be using different versions of Apache, PHP, mySQL, etc.) If there are bugs to be worked out, iron them out on your local working copy (i.e., go back to Step 2).
  6. If you’re satisfied that your dev site is good to go, then either checkout a copy to your live deployment site:

    % svn co http://svn.example.com/myproject/trunk ~/www.example.com

    or update the copy already in your deployment site:

    % svn up ~/www.example.com

  7. Double-check that the deployment site (http://www.example.com) is working to your satisfaction. If it’s not, go back to Step 2. If it is OK, you’re done!

The development cycle is now much simpler and safer. If you go on the road, just checkout a working copy onto your laptop, make your changes, and commit them back to the repository when you get the chance. If you (or your collaborator) made a mistake 6 revisions ago, just tell svn to checkout or update to an earlier revision.


return to topUse ssh/sftp instead of telnet/ftp

If you use telnet to log into your remote server, or ftp to transfer files between your local and remote machines, don’t. Use ssh instead; it’s much more secure. And if you’re accustomed to typing in a username and password when you login with ssh, you’re only creating more work for yourself. It only takes two minutes to set things up properly on your local and remote machines to permit secure non-passworded sessions using public/private key authentication. Do it today! Here’s how:

  1. On your local system do this:

    % ssh-keygen -t rsa

    When asked, just go with the default settings. This will generate your public key (a long string of text) and automatically insert it into the file ~/.ssh/id_rsa.pub.

  2. Now log into your remote system and open (and, if necessary, create) the file ~/.ssh/authorized_keys. Copy the public key that was generated on your local machine (e.g., % cat ~/.ssh/id_rsa.pub) and paste it at the end of this file. Make this file readable only to you, by doing:

    % chmod 700 ~/.ssh/.

  3. Log out of your remote system.
  4. Connect to your remote system via ssh:

    % ssh yourname@yourremotesystem.com

    You should be greeted by something like this:

    The authenticity of host 'yourremotesystem.com' (xxx.xxx.xxx.xxx) can't be established. RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:.... Are you sure you want to continue connecting (yes/no)?

    Type 'yes' and RETURN. You should see something like this:

    Warning: permanently added 'yourremotesystem.com, xxx.xxx.xxx.xxx' (RSA) to the list of known hosts.

That's it! From now on you'll be able to login with ssh (and use sftp) with ease, without ever having to type in a password.

return to topDreamHost: can’t connect to webpanel

Problem:Unable to access Dreamhost’s (DH) webpanel from my home computer (Mac OS 10.4). When pointing my browser to panel.dreamhost.com I’d invariably get a "connection timed out" error. The output from traceroute suggested to me that the problem was at DH’s end:

% traceroute panel.dreamhost.com traceroute to panel.dreamhost.com (, 64 hops max, 40 byte packets 1 [...] 2 [...] 3 at-1-2-0-1719.core-rtr2.bos.verizon-gni.net ( 25.461 ms 26.321 ms 25.591 ms 4 so-0-2-0-0.bb-rtr2.bos.verizon-gni.net ( 25.988 ms 26.193 ms 31.568 ms 5 0.so-5-2-0.xl2.bos4.alter.net ( 32.311 ms 27.812 ms 27.315 ms 6 0.so-6-0-0.xl2.lax15.alter.net ( 121.418 ms 121.219 ms 120.668 ms 7 pos7-0.gw2.lax15.alter.net ( 111.140 ms 112.440 ms 112.517 ms 8 internapgige-gw.customer.alter.net ( 115.499 ms 112.022 ms 111.304 ms 9 border1.po2-bbnet2.ext1a.lax.pnap.net ( 112.983 ms 112.325 ms 112.211 ms 10 newdream-1.border1.ext1a.lax.pnap.net ( 347.647 ms 114.602 ms 208.394 ms 11 lb1.sd.dreamhost.com ( 114.535 ms 119.165 ms 119.207 ms 12 * * * 13 * * * 14 *^C %

DH tech support recommended that I flush my DNS cache:

% lookupd -flushcache

This didn’t help. After many more back-and-forths with tech support, including elevation to a "level two queue" (??), I finally tracked down the problem to my /etc/hosts file.

Cause: An invalid entry in my /etc/hosts file. Earlier this year DH had recommended that its customers add these lines to their /etc/hosts file, as insurance against DH’s DNS server going down: panel.dreamhost.com dreamhoststatus.com dreamhost.com www.dreamhost.com www.dreamhoststatus.com

The trouble is that DH had subsequently changed the IP address of panel.dreamhost.com, without my knowing it.

Fix: Comment out those lines and restart the DNS daemon:

% sudo vi /etc/hosts [to insert ’#’ in front of those lines...]

% sudo killall -HUP lookupd

Moral #1: if you’re going to add IP addresses to your /etc/hosts, it’s up to you to make sure those entries are up to date. If your ISP tells you to add such-and-such to that file, don’t count on them keeping you informed of any changes later on.

Moral #2: When troubleshooting DNS problems, always remember: if traceroute chokes on a remote server along the route, it might very well be a problem with your DNS configuration. Always check /etc/hosts first!

return to topNotes

More stuff I tend to forget...

Launch the MySQL daemon
% sudo /usr/local/bin/mysqld_safe [type ^Z] % bg
Stop the MySQL daemon
% mysqladmin shutdown -p [type in the password when prompted] % mysqld --skip-grant-tables
Change MySQL’s root password
% mysqladmin -u root -p password NEW_PASSWORD [type in the old password when prompted]
Backup a MySQL database
% mysqldump --opt -uUSERNAME -pPASSWORD -h HOSTNAME DBNAME > db.txt % zip db.zip db.txt

If you can’t remember USERNAME, PASSWORD, HOSTNAME, or DBNAME, you can find them in (for example) your WordPress wp-config.php file.

During installation phpMyAdmin says "#2002 — The server is not responding (or the local MySQL server’s socket is not correctly configured)":

I don’t claim to understand this one at all. But some combination of these seem to do the trick:

  1. Log on as root and create the file /etc/my.cnf. Insert these lines into the file:
    [client] socket=/var/mysql/mysql.sock [mysqld] socket=/var/mysql/mysql.sock
  2. Look at /etc/php.ini and find the line concerning "Default socket name for local MySQL connects". There should be nothing after the equals sign:
    mysql.default_socket =
  3. Execute phpinfo() and examine all the references to mysql. Look for any weirdnesses and inconsistencies there. Make sure the mysql socket names and directory paths all make sense with your php installation.
Check your PHP configuration
Create a text file containing the following single line of code:
<?php phpinfo() ?>
Name the file something useful like phpinfo.php and load it into your browser.