Friday, December 4, 2009

Timezone List in MySQL

Below you can find a list of all timezones and their offsets.

CREATE TABLE IF NOT EXISTS `time_zone` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL DEFAULT '',
  `offset` float NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=40 ;

INSERT INTO `time_zone` (`id`, `name`, `offset`) VALUES
(1, '(-12:00) Enitwetok, Kwajalien', -12),
(2, '(-11:00) Midway Island, Samoa', -11),
(3, '(-10:00) Hawaii', -10),
(4, '(-9:30) French Polynesia', -9.5),
(5, '(-9:00) Alaska', -9),
(6, '(-8:00) Pacific Time (US & Canada)', -8),
(7, '(-7:00) Mountain Time (US & Canada)', -7),
(8, '(-6:00) Central Time (US & Canada), Mexico City', -6),
(9, '(-5:00) Eastern Time (US & Canada), Bogota, Lima', -5),
(10, '(-4:00) Atlantic Time (Canada), Caracas, La Paz', -4),
(11, '(-3:30) Newfoundland', -3.5),
(12, '(-3:00) Brazil, Buenos Aires, Falkland Is.', -3),
(13, '(-2:00) Mid-Atlantic, Ascention Is., St Helena', -2),
(14, '(-1:00) Azores, Cape Verde Islands', -1),
(15, 'Casablanca, Dublin, London, Lisbon, Monrovia', 0),
(16, '(+1:00) Brussels, Copenhagen, Madrid, Paris', 1),
(17, '(+2:00) Kaliningrad, South Africa', 2),
(18, '(+3:00) Baghdad, Riyadh, Moscow, Nairobi', 3),
(19, '(+3:30) Tehran', 3.5),
(20, '(+4:00) Abu Dhabi, Baku, Muscat, Tbilisi', 4),
(21, '(+4:30) Kabul', 4.5),
(22, '(+5:00) Ekaterinburg, Karachi, Tashkent', 5),
(23, '(+5:30) Bombay, Calcutta, Madras, New Delhi', 5.5),
(24, '(+5:45) Kathmandu', 5.75),
(25, '(+6:00) Almaty, Colombo, Dhaka', 6),
(26, '(+6:30) Yangon, Naypyidaw, Bantam', 6.5),
(27, '(+7:00) Bangkok, Hanoi, Jakarta', 7),
(28, '(+8:00) Hong Kong, Perth, Singapore, Taipei', 8),
(29, '(+8:45) Caiguna, Eucla', 8.75),
(30, '(+9:00) Osaka, Sapporo, Seoul, Tokyo, Yakutsk', 9),
(31, '(+9:30) Adelaide, Darwin', 9.5),
(32, '(+10:00) Melbourne, Papua New Guinea, Sydney', 10),
(33, '(+10:30) Lord Howe Island', 10.5),
(34, '(+11:00) Magadan, New Caledonia, Solomon Is.', 11),
(35, '(+11:30) Burnt Pine, Kingston', 11.5),
(36, '(+12:00) Auckland, Fiji, Marshall Island', 12),
(37, '(+12:45) Chatham Islands', 12.75),
(38, '(+13:00) Kamchatka, Anadyr', 13),
(39, '(+14:00) Kiritimati', 14);

Country List in MySQL

Below you'll find a list of all the countries in the world. Useful for utilizing them on your web site.

CREATE TABLE IF NOT EXISTS `country` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=242 ;

INSERT INTO `country` (`id`, `name`) VALUES
(1, 'United States'),
(2, 'Canada'),
(3, 'Mexico'),
(4, 'Afghanistan'),
(5, 'Albania'),
(6, 'Algeria'),
(7, 'Andorra'),
(8, 'Angola'),
(9, 'Anguilla'),
(10, 'Antarctica'),
(11, 'Antigua and Barbuda'),
(12, 'Argentina'),
(13, 'Armenia'),
(14, 'Aruba'),
(15, 'Ascension Island'),
(16, 'Australia'),
(17, 'Austria'),
(18, 'Azerbaijan'),
(19, 'Bahamas'),
(20, 'Bahrain'),
(21, 'Bangladesh'),
(22, 'Barbados'),
(23, 'Belarus'),
(24, 'Belgium'),
(25, 'Belize'),
(26, 'Benin'),
(27, 'Bermuda'),
(28, 'Bhutan'),
(29, 'Bolivia'),
(30, 'Bophuthatswana'),
(31, 'Bosnia-Herzegovina'),
(32, 'Botswana'),
(33, 'Bouvet Island'),
(34, 'Brazil'),
(35, 'British Indian Ocean'),
(36, 'British Virgin Islands'),
(37, 'Brunei Darussalam'),
(38, 'Bulgaria'),
(39, 'Burkina Faso'),
(40, 'Burundi'),
(41, 'Cambodia'),
(42, 'Cameroon'),
(44, 'Cape Verde Island'),
(45, 'Cayman Islands'),
(46, 'Central Africa'),
(47, 'Chad'),
(48, 'Channel Islands'),
(49, 'Chile'),
(50, 'China, Peoples Republic'),
(51, 'Christmas Island'),
(52, 'Cocos (Keeling) Islands'),
(53, 'Colombia'),
(54, 'Comoros Islands'),
(55, 'Congo'),
(56, 'Cook Islands'),
(57, 'Costa Rica'),
(58, 'Croatia'),
(59, 'Cuba'),
(60, 'Cyprus'),
(61, 'Czech Republic'),
(62, 'Denmark'),
(63, 'Djibouti'),
(64, 'Dominica'),
(65, 'Dominican Republic'),
(66, 'Easter Island'),
(67, 'Ecuador'),
(68, 'Egypt'),
(69, 'El Salvador'),
(70, 'England'),
(71, 'Equatorial Guinea'),
(72, 'Estonia'),
(73, 'Ethiopia'),
(74, 'Falkland Islands'),
(75, 'Faeroe Islands'),
(76, 'Fiji'),
(77, 'Finland'),
(78, 'France'),
(79, 'French Guyana'),
(80, 'French Polynesia'),
(81, 'Gabon'),
(82, 'Gambia'),
(83, 'Georgia Republic'),
(84, 'Germany'),
(85, 'Gibraltar'),
(86, 'Greece'),
(87, 'Greenland'),
(88, 'Grenada'),
(89, 'Guadeloupe (French)'),
(90, 'Guatemala'),
(91, 'Guernsey Island'),
(92, 'Guinea Bissau'),
(93, 'Guinea'),
(94, 'Guyana'),
(95, 'Haiti'),
(96, 'Heard and McDonald Isls'),
(97, 'Honduras'),
(98, 'Hong Kong'),
(99, 'Hungary'),
(100, 'Iceland'),
(101, 'India'),
(102, 'Iran'),
(103, 'Iraq'),
(104, 'Ireland'),
(105, 'Isle of Man'),
(106, 'Israel'),
(107, 'Italy'),
(108, 'Ivory Coast'),
(109, 'Jamaica'),
(110, 'Japan'),
(111, 'Jersey Island'),
(112, 'Jordan'),
(113, 'Kazakhstan'),
(114, 'Kenya'),
(115, 'Kiribati'),
(116, 'Kuwait'),
(117, 'Laos'),
(118, 'Latvia'),
(119, 'Lebanon'),
(120, 'Lesotho'),
(121, 'Liberia'),
(122, 'Libya'),
(123, 'Liechtenstein'),
(124, 'Lithuania'),
(125, 'Luxembourg'),
(126, 'Macao'),
(127, 'Macedonia'),
(128, 'Madagascar'),
(129, 'Malawi'),
(130, 'Malaysia'),
(131, 'Maldives'),
(132, 'Mali'),
(133, 'Malta'),
(134, 'Martinique (French)'),
(135, 'Mauritania'),
(136, 'Mauritius'),
(137, 'Mayotte'),
(139, 'Micronesia'),
(140, 'Moldavia'),
(141, 'Monaco'),
(142, 'Mongolia'),
(143, 'Montenegro'),
(144, 'Montserrat'),
(145, 'Morocco'),
(146, 'Mozambique'),
(147, 'Myanmar'),
(148, 'Namibia'),
(149, 'Nauru'),
(150, 'Nepal'),
(151, 'Netherlands Antilles'),
(152, 'Netherlands'),
(153, 'New Caledonia (French)'),
(154, 'New Zealand'),
(155, 'Nicaragua'),
(156, 'Niger'),
(157, 'Niue'),
(158, 'Norfolk Island'),
(159, 'North Korea'),
(160, 'Northern Ireland'),
(161, 'Norway'),
(162, 'Oman'),
(163, 'Pakistan'),
(164, 'Panama'),
(165, 'Papua New Guinea'),
(166, 'Paraguay'),
(167, 'Peru'),
(168, 'Philippines'),
(169, 'Pitcairn Island'),
(170, 'Poland'),
(171, 'Polynesia (French)'),
(172, 'Portugal'),
(173, 'Qatar'),
(174, 'Reunion Island'),
(175, 'Romania'),
(176, 'Russia'),
(177, 'Rwanda'),
(178, 'S.Georgia Sandwich Isls'),
(179, 'Sao Tome, Principe'),
(180, 'San Marino'),
(181, 'Saudi Arabia'),
(182, 'Scotland'),
(183, 'Senegal'),
(184, 'Serbia'),
(185, 'Seychelles'),
(186, 'Shetland'),
(187, 'Sierra Leone'),
(188, 'Singapore'),
(189, 'Slovak Republic'),
(190, 'Slovenia'),
(191, 'Solomon Islands'),
(192, 'Somalia'),
(193, 'South Africa'),
(194, 'South Korea'),
(195, 'Spain'),
(196, 'Sri Lanka'),
(197, 'St. Helena'),
(198, 'St. Lucia'),
(199, 'St. Pierre Miquelon'),
(200, 'St. Martins'),
(201, 'St. Kitts Nevis Anguilla'),
(202, 'St. Vincent Grenadines'),
(203, 'Sudan'),
(204, 'Suriname'),
(205, 'Svalbard Jan Mayen'),
(206, 'Swaziland'),
(207, 'Sweden'),
(208, 'Switzerland'),
(209, 'Syria'),
(210, 'Tajikistan'),
(211, 'Taiwan'),
(212, 'Tahiti'),
(213, 'Tanzania'),
(214, 'Thailand'),
(215, 'Togo'),
(216, 'Tokelau'),
(217, 'Tonga'),
(218, 'Trinidad and Tobago'),
(219, 'Tunisia'),
(220, 'Turkmenistan'),
(221, 'Turks and Caicos Isls'),
(222, 'Tuvalu'),
(223, 'Uganda'),
(224, 'Ukraine'),
(225, 'United Arab Emirates'),
(226, 'Uruguay'),
(227, 'Uzbekistan'),
(228, 'Vanuatu'),
(229, 'Vatican City State'),
(230, 'Venezuela'),
(231, 'Vietnam'),
(232, 'Virgin Islands (Brit)'),
(233, 'Wales'),
(234, 'Wallis Futuna Islands'),
(235, 'Western Sahara'),
(236, 'Western Samoa'),
(237, 'Yemen'),
(238, 'Yugoslavia'),
(239, 'Zaire'),
(240, 'Zambia'),
(241, 'Zimbabwe');

Saturday, November 21, 2009

Setting up a local Virtual Host using WAMP/Apache

Setting up a virtual host will allow you to essentially run an exact copy of your site locally.  You'll need to decide on a local domain name.  My standard is to only maintain the primary name.  So for instance if the site is http://www.domain.com then I will use http://domain/ for the local copy.  Some people decide to use http://domain.loc/.

Once you've decided, open C:\Windows\System32\drivers\etc and open the hosts file.

It should look like this when opened:

127.0.0.1       localhost
::1             localhost

Add a new line

127.0.0.1       localhost
127.0.0.1       domain
::1             localhost

Replace "domain" with "domain.loc" if you want.

Next you're going to need to locate your Apache installation. In WAMP, you're going to need to open C:\wamp\bin\apache\Apache2.2.11\conf\extra\httpd-vhosts.conf and add the line:



    ServerAdmin webmaster@localhost
    DocumentRoot "c:/wamp/www/domain"
    ServerName domain
    ErrorLog "logs/domain.log"
    CustomLog "logs/domain.log" common
    
        Options Indexes FollowSymLinks
        AllowOverride all
        Order Allow,Deny
        Allow from all
        Allow from 127.0.0.1
    

AFTER

NameVirtualHost *:80



    DocumentRoot "c:/wamp/www"
    ServerName localhost

If you're using .loc, all you should have to do is add .loc to the ServerName, NOT to the paths defining the location to load.

Friday, November 20, 2009

Regex Library

I was doing a search for some regex that I could modify to meet my specific needs for a project (I haven't learned it yet). I stumbled on this site that has tons and tons of regex examples. I figured it was a link I definitely wanted to keep so I'd post it here. Who knows, maybe you'll find it useful too.

http://www.roscripts.com/PHP_regular_expressions_examples-136.html

Wednesday, November 18, 2009

PHP Force File Download

When setting up a file download on a web site you never want to link to the file directly.  By linking to the file directly you're letting the browser determine the best action to take.  If the file is capable of being opened in the browser (.txt, .jpg, .pdf) then that's what the browser will do.  However you can easily force the browser to give a download prompt.

$file = '/home/user/public_html/file.zip';

header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: '.gmdate ('D, d M Y H:i:s', filemtime ($file)).' GMT');
header('Cache-Control: private',false);
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($file));
header('Connection: close');

readfile($file);
exit();

Run PHP via Cron

It's simple, run the following command using an absolute path to the file.

php -q /home/user/public_html/file.php

Friday, October 2, 2009

Warning: number_format() expects parameter 1 to be double

I ran into this error when I was working on the Google Custom Search API.  I've never run into it before.  I'm passing the estimated number of search results through to a number_format() which is currently passing through the number 10.  Unfortunately it's throwing this error.  I haven't figured it out yet, if anyone knows please post the solution so others can find it as well.

Tuesday, August 4, 2009

OpenX Javascript or CSS not loading

The get.max_value_length value of your web server settings does not allow to use long URL paths. This might lead to JavaScript and CSS files not properly being included.  To fix this you have 2 options:

  1. Modify your config setting for OpenX to disable combining/compressing CSS/JS files (this only effects the admin panel).



    [ui]combineAssets=0
  2. Modify php.ini to extend the length of your GET variable values.  The default setting for PHP 5.3 is 512.


How to find the locaion of php.ini

To find the location of your php.ini file, copy and paste the below code into a php file and upload it to your server.

<?php
phpinfo();
?>

When you access the file it will look like the below image. The red arrow is pointing to the location of your php.ini file. In Linux environments this is the most common location.


A quick guide to Smarty caching

When you first jump into Smarty Template Engine caching it can be a bit confusing and overwhelming till you get your feet wet and begin to understand some core concepts of how Smarty works.  Now that I'm beginning to understand it, I see how we can't live without it!

Basic Caching

Smarty has a _compiled folder which is used to cache the parsed versions of templates (ie - Smarty's "template code" is replaced with actual PHP and stored in the _compiled folder).  Smarty also has a _cache folder which will actually store individual templates.

For smaller web sites, Smarty's basic caching is very effective and simple.  You simply enable caching and define a lifetime

caching = 2; // lifetime is per cache

// set the cache_lifetime for index.tpl to 5 minutes
$smarty->cache_lifetime = 300;
$smarty->display('index.tpl');

// set the cache_lifetime for home.tpl to 1 hour
$smarty->cache_lifetime = 3600;
$smarty->display('home.tpl');

// NOTE: the following $cache_lifetime setting will not work when $caching = 2.
// The cache lifetime for home.tpl has already been set
// to 1 hour, and will no longer respect the value of $cache_lifetime.
// The home.tpl cache will still expire after 1 hour.
$smarty->cache_lifetime = 30; // 30 seconds
$smarty->display('home.tpl');
?>

Group Caching

Smarty offers the ability to group your templates.  So if you need to lets say, clear the templates for the news section of your web site, you can simply execute $smarty->clear_cache(null,'news'); and all of your news template's caches will be cleared.  This same technique can be utilized for caching dynamic pages with a ?id= within the URL.  You can apply multiple group tags to identify a group, or an individual element. In the below example, index.tpl will be used to view all articles, but to decrease database load you want to cache each article's contents. By defining news|$article_id you are creating a sub group of news for each article id, meaning they won't be overwritten by other articles.

caching = 1;

$my_cache_id = $_GET['article_id'];

if(!$smarty->is_cached('index.tpl', 'news|'.$my_cache_id)) {
    // No cache available, do variable assignments here.
    $contents = get_database_contents();
    $smarty->assign($contents);
}

$smarty->display('index.tpl', 'news|'.$my_cache_id);
?>

By utilizing the is_cached() function we see the core benefit of Smarty. If the cache exists, we don't need to run queries to or any parsing on the article itself, so we can skip all of this. This is very beneficial to reduce SQL server load. It's much less server processing to see if a file exists than it is to run queries/parsing on an article.

That's the basics of Smarty caching.

Thursday, July 30, 2009

Smarty syntax error: unrecognized tag 'dynamic' and Block functions

I ran into this error while working with Smarty caching.  This error is caused by a "dynamic" block function not being registered with Smarty.  A block function is a function which essentially will take a section of content and execute a function on that content.  Because of this, Smarty will not cache it.  Notice in this example the smarty_block_dynamic function simply returns the content.  Because it was defined as "dynamic" you can use the Smarty dynamic tags to wrap content and avoid the caching engine.

This is not a default tag in Smarty and will fail if you have not registered the block function as provided in the example.

Tuesday, July 21, 2009

Feedburner Custom URL

Feedburner offers a custom URL service which will allow for you to link to any domain/subdomain combination instead of feeds*.feedburner.com. The good news is anyone can do it. You can find the Branding settings by logging info feedburner and clicking "My Account" and then "My Brand".

Google will then offer instructions on how to do this. On a shared hosting service you can submit a support request to have the DNS entries made for you, on a dedicated/self hosted you can do it yourself. These changes have to resolve just as any other DNS, so the effects are not immediate.

Note: I just changed web hosts and it appears that the DNS entry url that Google provided changed as well. That leads me to believe that if your primary domain changes nameservers that Google would issue a new entry value.

Monday, July 20, 2009

How to execute PHP files via cron in CPanel

There are two ways that I know of (I'm sure there's more) to execute php files within cron commands.

php -q /home/USER/public_html/cron/path-to-file.php

This first method will utilize relative paths. Meaning if within this file you utilize a require_once(../file.php); it will load the file accordingly. Whereas if you use the second route

/usr/bin/php -q /home/USER/public_html/cron/path-to-file.php

you must utilize absolute paths within your file. Including a file using a relative path (like ../) will throw an error, you must define the full path of the file. Save yourself a headache when you go to transfer hosts, utilize the first method.

Wednesday, July 15, 2009

Why do web developers support outdated browsers?

At the date of this post, IE 8 has been out for 4 months and IE 7 for 2.5 years.  Why am I still seeing threads about designing for IE 6?

The reality of it is that your average person knows little about what a browser is, and why it's important to keep your software up to date.  Part of this reason is that nothing ever breaks when they are running outdated technology.  Developers need to keep moving the web forward and stop supporting older browsers.  A step in the right direction is telling your user when their browser is out of date.  In our next update, users not using the latest major version of Chrome, IE, and Firefox will have a notice on their screen explaining that their browser is out of date with a provided link on how to upgrade.

By implementing features like this, you're saving yourself time and educating your visitors.

UPDATE: The day after I posted this Google announced they are discontinuing support for IE 6.

Tuesday, July 14, 2009

CSS Syntax Formatting Standards

Designing for readability is very important. Not only can it cut down on your own development time when you perform maintenance or make improvements to older sections of your site that you haven't seen in a while, but it also makes it much easier for other developers to read your code.

Before we get into standards its important to note that tabbing, comments, and spacing increase the file size of your style sheets. Before you implement these kinds of standards, ensure you have some kind of compression in place (I recommend Minify for both CSS and JS) to keep the file size under control. Minify is extremely important for shared hosts such as HostGator that do not permit gZip compression.

Tabbing
Always tab/nest your code accordingly. Below is a code snipplet from some CSS on one of our main templates. Notice it's easier to read when it's tabbed as you can quickly determine which elements are children of which. In a larger stylesheet this really helps because you can see #hd .nav and know that's not what you need and skip down to the next section.

#hd .nav {
  border-color: #223f4a;
  border-style: solid;
  border-width: 1px 1px 0 1px;
 }
  #hd .nav ul {
   margin-left: 98px;
  }
   #hd .nav ul li {
    float: left;
    margin: 2px 0 6px 0;
    padding-right: 2px;
    position: relative;
   }
CSS Selectors
Using the same snipplet above, you'll notice that our CSS Selector's anchor is unique and not a class. When available, use a unique anchor rather than a class. This will help prevent CSS from effecting elements it was not intended to effect.

Also notice that within our selector, while the 3rd child could very easily be called #hd li instead of #hd .nav ul li, because of our tabbing and to increase readability we generally include tree elements in the selectors. This will also ensure that it only effects exactly what it was intended to effect rather than effecting any other lists.

Selectors by Class
In your CSS selectors it's better practice to use classes rather than elements.  For instance, the below example is restricted to a ul element and cannot be used on a ol element.  As more useful tags are being included with CSS, get in the habit of designing your code for flexability, you'll be surprised at the different ways you utilize the same CSS across multiple elements.

ul.bullet {
 background-image: url(resource/bullet.gif);
 background-position: 4px 6px;
}

Monday, July 13, 2009

CSS Form Element Standards

Having standards set within your code is probably one of the single most effective habits you can have as a developer. Even if your code is bad, having a standard way of executing things you know exactly how your code will be effected, and it makes it easier to improve your code because it's handled consistently across the whole site.  Consistency is key, and that applies in all areas, not just in your user experience.

With us in the middle of redesigning one of our sites we have moved our policy of creating standards to CSS, and really taken it to the next level.

Element Sizes
Form fields should not all be the same size.  Different form elements accept different types of data of varying lengths.  Because consistency is important both within your code and for the user experience we have implemented form standard sizes.

/*
 * Input
 */
.form input.small {
 width: 100px;
}
.form input.medium {
 width: 200px;
}
.form input.large {
 width: 300px;
}
.form textarea.small {
 width: 250px;
 height: 80px;
}
/*
 * Textarea
 */
.form textarea.medium {
 width: 360px;
 height: 120px;
}
.form textarea.large {
 width: 360px;
 height: 220px;
}
.form textarea.xlarge {
 width: 360px;
 height: 350px;
}

This allows us to globally control the size of our forms and our forms to always have consistent sizes so we don't end up with 4-5 different size fields on a single form.

Element Styles
 While it's generally not a good idea to style form elements too much, there are some good things you can do to enhance the user experience.  Web sites are becoming crisper, cleaner, and more interactive - Web 2.0.  To follow this trend we have modified all of our form elements to have a light gray border with 4px padding on the element.  The padding is there to emphasize focus on the content of the element.  By making the select box larger we make it easier to click on and make it easier to read because of the extra spacing between the form element borders and the text itself.


To make our forms for interactive, we use the psuedo class focus to make the border slightly darker.  The objective is to highlight the field but keep the focus on the content itself, thus the border is not solid black, but only a darker gray.

With 47% of the web using Firefox (according to W3Schools), using these styles will work very well in accordance with select boxes.

Sunday, July 12, 2009

Multiple MySQL statements with a single query

To run multiple statements within a single query you simply need to separate the statements with a semicolon to signify the end of the statement.

UPDATE `inventory` SET `value` = 2 WHERE `name` = "Strawberries";
UPDATE `inventory` SET `value` = 4 WHERE `name` = "Blueberries"

This is the best route to take with any ALTER TABLE or UPDATE queries.

For inserting multiple rows you should do the following.

INSERT INTO `inventory` (`id`, `name`, `value`) VALUES
(1, 'Strawberries', 4),
(2, 'Blueberries', 4);

Unfortunately, while PHP's mysqli_multi_query does support multiple queries, mysql_query does not.  A workaround for this is to explode the sql string by a semicolon and execute each query manually.

$sql = "UPDATE `inventory` SET `value` = 2 WHERE `name` = \"Strawberries\";
UPDATE `inventory` SET `value` = 4 WHERE `name` = \"Blueberries\"";

$pieces = explode(";", $sql);
foreach ($pieces as $query) {
     mysql_query($query) or die(mysql_error());
}

This can easily be put into a function to keep your code clean while still getting the usability you're looking for.

Valid RSS 2.0 Generator Class

I've been searching Google and couldn't find anything except documentation. So I'm posting this here in hopes that it will help someone else from having to go through a few hours of Trial and Error getting their feed to validate. This is tested only to pass the W3.org RSS 2.0 Validator.

I initially programmed it with required key values and things utilizing our custom api, but removed all of that for a stripped down basic version. If anyone has any updates/improvements to this feed or has any classes for other kinds of feeds I'd definitely be interested in hearing about them.

Post a comment if you use this and find it useful.

<?php
class FeedRss {
 
 var $feed = "";
 
 /*===========================================*/
 /* Public Functions
 /*===========================================*/
 public function create ($paramList) {
     $paramList = $this->cleanParamList($paramList);    
 
     $this->feed .= "<?xml version='1.0' encoding='iso-8859-1'?>\n";
     $this->feed .= "    <rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>\n";
     $this->feed .= "        <channel>\n";
     $this->feed .= "            <atom:link href='".$paramList['location']."' rel='self' type='application/rss+xml' />\n";
     $this->feed .= "            <title>".$paramList['title']."</title>\n";
  
  if (array_key_exists("copyright", $paramList)) {
       $this->feed .= "            <copyright>".$paramList['copyright']."</copyright>\n";
  }
  
  $this->feed .= "            <link>".$paramList['link']."</link>\n";
  $this->feed .= "            <description>".$paramList['description']."</description>\n";
  
  if (is_numeric($paramList["updated"])) {
   $this->feed .= "            <lastbuilddate>".date('D, d M Y H:i:s O', $paramList['updated'])."</lastBuildDate>\n";
  } else {
   $this->feed .= "            <lastbuilddate>".$paramList['updated']."</lastBuildDate>\n";
  }
  
  $this->feed .= "            <language>".$paramList['language']."</language>\n";
  
  if (array_key_exists("image", $paramList)) {
   $imageInput = array_merge($paramList["image"], array(
    "title" => $paramList["title"]
   ));
   $this->addImage($imageInput);
  }
 }
 
 public function addItem ($paramList) {
  $paramList = $this->cleanParamList($paramList);    
 
  $this->feed .= "        <item>\n";
  $this->feed .= "            <title><![CDATA[".$paramList['title']."]]></title>\n";
  $this->feed .= "            <link>".$paramList['link']."</link>\n";
  $this->feed .= "            <description><![CDATA[".$paramList['description']."]]></description>\n";
  
  if (array_key_exists("author", $paramList)) {
   $this->feed .= "            <author>".$paramList['author']."</author>\n";
  }
  
  if (array_key_exists("guid", $paramList)) {
   $this->feed .= "            <guid>".$paramList['guid']."</guid>\n";
  }
  
  if (is_numeric($paramList["updated"])) {
   $this->feed .= "            <pubdate>".date('D, d M Y H:i:s O', $paramList['updated'])."</pubDate>\n";
  } else {
   $this->feed .= "            <pubdate>".$paramList['updated']."</pubDate>\n";
  }
  
  if (array_key_exists("category", $paramList)) {
   $this->feed .= "            <category>".$paramList['category']."</category>\n";
  }
  
  $this->feed .= "        </item>\n";
 }
 
 public function get () {
  $this->feed .= "        </channel>\n";
  $this->feed .= "    </rss>";
  
  return $this->feed;
 }
 
 /*===========================================*/
 /* Private Functions
 /*===========================================*/
 
 private function addImage ($paramList) {
  $this->feed .= "            <image>\n";
  $this->feed .= "                <url>".$paramList["url"]."</url>\n";
  $this->feed .= "                <width>".$paramList["width"]."</width>\n";
  $this->feed .= "                <height>".$paramList["height"]."</height>\n";
  $this->feed .= "                <title>".$paramList["title"]."</title>\n";
  $this->feed .= "                <link>".$paramList["link"]."</link>\n";
  $this->feed .= "            </image>\n";
 }    
 private function cleanParamList ($paramList) {
  $ignoreParam = array("updated", "description");
  
  $replaceList = array(
   "&" => "&amp;",
   "–" => "&ndash;",
   "—" => "&mdash;",
   "¡" => "&iexcl;",
   "¿" => "&iquest;",
   "«" => "&raquo;",
   "<" => "&lt;",
   ">" => "&gt;",
   "$" => "&#36;",
   "»" => "&laquo;",
   "¢" => "&cent;",
   "©" => "&copy;",
   "÷" => "&divide;",
   "ยต" => "&micro;",
   "·" => "&middot;",
   "¶" => "&para;",
   "±" => "&plusmn;",
   "€" => "&euro;",
   "£" => "&pound;",
   "®" => "&reg;",
   "§" => "&sect;",
   "¥" => "&yen;",
   "’" => "'",
   "“" => "\"",
   "”" => "\""
  );
  
  foreach ($paramList as $key => $value) {
   if (!in_array($key, $ignoreParam)) {
    $paramList[$key] = str_replace(array_keys($replaceList), array_values($replaceList), $str);
   }
  }
  
  return $paramList;
 }
}
?>


Usage:
//if writing to a file via cron or any other method, comment out this header
header("Content-Type: application/xml; charset=ISO-8859-1");

$feed->create(array(
 "title" => "Domain.com News",
 "copyright" => "Copyright (c) ".date("Y")." Site Inc. All rights reserved.",
 "link" => "http://www.domain.com",
 "location" => "http://feed.domain.com/feed.xml",
 "description" => "All of the latest news",
 "language" => "en-us",
 "image" => array(
 "url" => "http://www.domain.com/image/branding/rss-logo.jpg",
 "height" => 50,
 "width" => 50,
 "title" => "RSS feed from Domain.com",
 "link" => "http://www.domain.com"
)
));

//add as many items as you'd like
$feed->addItem(array(
 "title" => $news["title"],
 "link" => "http://www.domain.com/index.php?sec=news&act=view&id=".$news["id"],
 "guid" => "http://www.domain.com/index.php?sec=news&act=view&id=".$news["id"],
 "description" => $tempNewsItem["preview_cache"],
 "author" => "news@domain.com (author)", //for proper validation you should have an email AND an author's name of some sort
 "category" => "Computer Hardware",
 "updated" => $news["posted"]
));

//this can also be used to write to a file
echo $feed->get(); 

Saturday, July 11, 2009

What's the point of RSS?

This may sound like a stupid question to those that use it, but having just started using it myself I can understand the perspective.  RSS seems to confusing and pointless when you don't know anything about it.  In reality, it's rather simple, and extremely powerful.

RSS is a method of delivering your content, whether in full or partial, to an external source (a visitor's RSS reader or another web site) without them having to visit your site.  Having just become an RSS fan with my favorite reader, SharpReader, I now understand the importance of this increased usability.  I post news on our gaming network and have over 40 feeds that I check every day.  If I were to check each of these sites every day, it would take hours and I wouldn't do it.  By having these RSS feeds these sites are ensuring that their content is delivered to me whether I check their site or not.  Generally, RSS feeds only contain a snipplet, or preview and a Read More link to visit their web site.

But what about losing traffic?  The answer is, you're not losing traffic.  Of the sites I have feeds for I would essentially only check a handful of them if it weren't for the feeds but because of the feeds I am kept up to date on all of them.  And with FeedBurner (now owned by Google) offering to track the number of subscribers I can't think of a reason why you wouldn't have an RSS feed on your site.



If you want to add one, I have posted a Valid RSS 2.0 Generator Class you can use.

Success Messages

In almost all of my web sites I have setup forms on our site so that upon submitting successfully, they would either show the success message at the top of the page you were just at or show only a success message on the page and nothing else. While this wasn't the best practice, I couldn't think of a better route to take. My partner recently presented a new idea. Instead of doing it that way and requiring more user input as to what the next step would be, he suggested that we use session variables to store the success message and forward the user to a page they would likely want to be on. So when sending an email, rather than loading a page that just says "Email Sent!", we forward them back to their inbox and show the success message there.
When doing this in PHP, if you use a header() forward your session variable will not save unless you force PHP to write the session. To do this, you would use session_write_close().
$_SESSION["success"] = "Your email has been sent"];
session_write_close(); //without this, the session variable will not be saved before header() executes

header("Location: http://www.domain.com/my-inbox/");

From the Ground Up

Through my years of web development experience I have made significant improvements to my style and methods of web programming along the way. I will use this blog to share my findings in all areas of web development including MySQL, PHP, Javascript, HTML, and CSS.