Making WordPress Tag Balancing Work with Exec-PHP

I use the WordPress plugin Exec-PHP to use PHP in my posts, but under normal circumstances if I do this with “WordPress should correct invalidly nested XHTML automatically” (a.k.a. tag balancing) checked in Settings > Writing, I get this nasty error whenever I try to use PHP:

Parse error: syntax error, unexpected ‘?’ in /home/thripp/public_html/wp-content/plugins/exec-php/includes/runtime.php(42) : eval()’d code on line 1

The solution the developer provides is to simply disable that feature. That’s fine most of the time, but I encountered a tricky situation where I needed to use PHP and have WordPress close open HTML tags which I simply could not close.

My posts on this blog are usually photos with short descriptions, but occasionally I write long articles which may go on for thousands of words. Up until last year, my tag, category, and archive pages displayed the full content of my posts. WordPress excerpts were unacceptable for two reasons: 1.) they are always 55 words; 2.) I use Post Thumb Revisited to auto-convert 800×600 images to 400×300 thumbnails, but it only converts them through the_content filter, not the_excerpt. While the excerpt length is customizable in WP 2.8 or newer, I am unwilling to upgrade from WPMU 2.7. What I really needed was an excerpt that used the_content, respected all HTML tags, worked with Exec-PHP, and let me customize the excerpt length.

Enter The Excerpt Reloaded. The plugin was 5 years old, so I found an updated version with bugfixes that was only 3 years old. I quickly wrote this code for my theme’s index.php file and left it this way until today:

if(is_category() || is_archive()) the_excerpt_reloaded(200, ‘all’, ‘content’,
     true, “<strong>… continue reading</strong>”, false, 1, false, false,
     ‘p’, ‘Click to see whole entry.’, true);
else the_content(__(‘… CONTINUE READING’));

This has been the best of both worlds. It cuts off the content at 200 words, so most of my photos do not have a “continue reading” link because my descriptions are under 200 words. Longer posts are cut off after 200 words, so my archive pages do not become unnecessarily long. I had to set the 8th argument of the_excerpt_reloaded, $fix_tags, to false, because I would get the same old Exec-PHP error if it was set to true. “No problem,” I thought. I already have tag balancing disabled in WordPress, so what could it hurt to disable it here?

Recently, however, I encountered an insidious bug when a post was cut at 200 words in the middle of a <strong> tag. The tag would never be closed, meaning the rest of the page would be bold! Take a look at this screenshot:

Unbalanced tags

What do you do about something like this? Obviously, there are many solutions. I could rewrite the offending article so the 201st word is not in the middle of an HTML tag. All I would have to do is put in a few filler words earlier in the article. I could enable tag balancing, write some code to check if each post contains PHP, and not use the_excerpt_reloaded in those cases. I could use custom fields on posts to determine which mode of behavior should be used. I could upgrade WordPress (oh god no). All of these solutions seem suboptimal.

Instead, I went to the problem’s source. If $fix_tags is true, the_excerpt_reloaded runs the content of the excerpt through balanceTags. What is balanceTags? A WordPress function in /wp-includes/formatting.php which activates force_balance_tags. What is force_balance_tags? A WordPress function in the same file which looks like hieroglyphics. All I wanted to do was force the function to ignore PHP, but I couldn’t figure it out. It wasn’t a simple matter of ignoring <?php ?> tags. My PHP tags often appear in the middle of other HTML tags. Here is the source code of a typical photo on my blog:

<img src=”http://thripp.com/files/photos/flash.jpg” alt=”<?php the_title(); ?>” />

I took this picture of raindrops falling at night, and my camera’s flash reflected off one of the raindrops. It looks like a star going supernova.

[­sniplet fuji-a360], <?php fexf(); ?>

<a href=”[­sniplet photos-path]stock/<?php echo fprm(); ?>-stock.jpg”>[­sniplet stock-dl-text]</a> (<?php fsze(fprm() . ‘-stock.jpg’); ?>) or <a href=”[­sniplet photos-path]stock/source/<?php echo fprm(); ?>-ss.jpg”>[­sniplet ss-dl-text-lc]</a> (<?php fsze(‘source/’ . fprm() . ‘-ss.jpg’); ?>).

[­sniplet stock-rights]

Looks pretty terrible, huh? I’ve got sniplets in there, custom PHP functions, concatenation, nesting… you name it. This template creates the file paths for the photos, source files, and stock versions right from the post title, because I upload the photos by FTP and follow a rigid file structure. The only reason there’s a full IMG tag at the top of each post is because Post Thumb Revisted won’t create the thumbnails to automatically generate my gallery without it. The template also displays the size of the source files and then extracts and displays the Exif data from the photos in my preferred format, which was extremely difficult to set up and is something I used to do manually. It runs itself, and the functions are really quite interesting.

Anyway, what I needed was a way to bypass force_balance_tags entirely, but only in regard to PHP code. I need the function to close dangling tags like <strong>, <em>, and <u> if the_excerpt_reloaded cuts the post in the middle of a tag.

After a lot of unsuccessful Google searches, I remembered that I solved a similar problem at the beginning of October in Tweet This 1.8. On the “Write Tweet” page, Tweet This uses a modified version of Jeff Roberson’s Linkify URL to delimit URLs with a space on each side (function tt_delimit_urls). A tweet like “Check out http://www.google.com/!” becomes “Check out http://www.google.com/ !”. Then, I use Ext-Conv-Links by Muhammad Arfeen to convert all long URLs to short URLs if the tweet is over 140 characters (class tt_shorten_urls). This works great for most URLs, but I discovered it breaks URLs containing underscores. http://en.wikipedia.org/wiki/South_Africa gets sent to the URL shortener http://en.wikipedia.org/wiki/South, which gets converted into http://bit.ly/bzLvSK_Africa, which doesn’t work at all. Totally unacceptable.

After many hours of torment trying to fix Jeff or Muhammad’s code, I decided to approach the problem from a different angle. Why not just replace underscores with something else on the way in, and then change them back to something else on the way out? Good programming doesn’t dance around problems, but I’ll take a practical solution that works over an idealistic solution that fails, any day. But what string to replace underscores with? I can’t use a special character or something that might be used in a tweet on purpose, because it will get converted into an underscore. After some thought, I settled on t9WGb5. It doesn’t look pretty, but it works, and I doubt any URL containing “t9WGb5” is ever going to be purposefully included in a tweet. So I proceeded to write statements like str_replace(‘t9WGb5’, ‘_’, $url) and str_replace(‘_’, ‘t9WGb5’, $url) at the necessary places throughout the code, and URLs with underscores worked like a charm. As an Easter egg, try writing a tweet over 140 characters containing a URL where you replace an underscore with “t9WGb5” yourself, for example, “test test test test test test test test test test test test test test test test test test test http://en.wikipedia.org/wiki/Southt9WGb5Africa”, then preview it on the Write Tweet page. Check the preview page for the short URL, i.e. http://bit.ly/cRLAis+, and you’ll see that your “t9WGb5” was converted to an underscore before the long URL was even sent to Bit.ly, as an artifact of my kludge-like solution.

Couldn’t the tag balancing problem be approached in the same way? Of course it could. A simple modification to /wp-includes/formatting.php did the trick. Right at the start of the force_balance_tags function, I replaced “<?php” and “?>” with “[![?php” and “?]!]” using str_replace, as follows:

function force_balance_tags( $text ) {
     $text = str_replace(array(‘<?php’, ‘?>’), array(‘[![?php’, ‘?]!]’), $text);
     $tagstack = array(); $stacksize = 0; $tagqueue = ”; $newtext = ”;

Then, at the end of the function, I change it all back:

// WP fix for the bug with HTML comments
     $newtext = str_replace(“< !–“,”<!–“,$newtext);
     $newtext = str_replace(“< !–“,”< !–“,$newtext);
     $newtext = str_replace(array(‘[![?php’, ‘?]!]’), array(‘<?php’, ‘?>’), $newtext);
     return $newtext;
}

All this happens either before or after Exec-PHP executes. I’m not sure when, but it doesn’t matter. My goal of being able to use tag balancing with Exec-PHP has been reached. I now have $strip_tags set to true in the_excerpt_reloaded and “WordPress should correct invalidly nested XHTML automatically” enabled in Settings > Writing, and all I have to do is re-apply the hack when I upgrade WordPress. It’s amazing what thinking outside the box gets you.

I can’t actually write “[![?php” or “?]!]” inside any post on my site, because my hack will convert those strings to real PHP code and they won’t be displayed. How did I display the code above? My actual /wp-includes/formatting.php file uses underscores instead of exclamation points. How did I include the sniplets in the example post without the Sniplets plugin executing them? Breaking the parser with the &shy; HTML entity. Simple.

Earlier, I talked about the functions I use in my photo template to automate display of file size and Exif data. Here are those functions:

function fsze($f = ‘simplicity-stock.jpg’, $p =
     ‘/home/thripp/public_html/wp-content/blogs.dir/2/files/photos/stock/’)
     {$n = array(‘Bytes’, ‘KB’, ‘MB’, ‘GB’); $p = $p . $f;
     if(file_exists($p)) $b = filesize($p);
          else $b = ‘1000’;
     echo round($b/pow(1000, ($i = floor(log($b, 1000)))), 2) . $n[$i];}

function fprm() {
     return str_replace(‘photo-‘, ”, preg_replace(‘/-+/’, ‘-‘,
          preg_replace(‘/[^a-z0-9-]/’, ‘-‘,
          strtolower(trim(str_replace(array(‘?’, ‘…’),
          array(”, ”), get_the_title()))))));}

function fexf() {
     $exif = exif_read_data(‘/home/thripp/public_html/wp-content/’ .
          ‘blogs.dir/2/files/photos/’ . fprm() . ‘.jpg’, 0, true);
     $shutter = $exif[‘EXIF’][‘ExposureTime’];
     $fnum = str_replace(‘f/’, ‘F’, $exif[‘COMPUTED’][‘ApertureFNumber’]);
     $focal = $exif[‘EXIF’][‘FocalLength’];
     $iso = $exif[‘EXIF’][‘ISOSpeedRatings’];
     $date = $exif[‘EXIF’][‘DateTimeOriginal’];
     $date = str_replace(‘:’, ‘-‘, substr($date, 0, 10)) . ‘T’ .
          substr($date, 11);
     if(substr($date, 0, 4) < = 2007) {           $id = substr($date, 0 , 10) . '_' . substr($date, 11, 2) .           'h' . substr($date, 14, 2) . 'm' . substr($date, 17);}      elseif(substr($date, 0, 4) >= 2008) {
          $id = str_replace(‘-‘, ”, substr($date, 0 , 10)) . ‘-‘ .
          str_replace(‘:’, ”, substr($date, 11)) . ‘rxt’;}
     $md = str_replace(‘-‘, ”, substr($date, 5, 5));
     $hms = str_replace(‘:’, ”, substr($date, 11));
     if(substr($date, 0, 4) == 2004) {
          if(($md < 0404) || ($md == '0404' && $hms < 020000) ||           ($md > 1031) || ($md == ‘1031’ && $hms > 020000))
               $ldate = $date . ‘-05’;
          else $ldate = $date . ‘-04’;}
     if(substr($date, 0, 4) == 2005) {
          if(($md < 0403) || ($md == '0403' && $hms < 020000) ||           ($md > 1030) || ($md == ‘1030’ && $hms > 020000))
               $ldate = $date . ‘-05’;
          else $ldate = $date . ‘-04’;}
     if(substr($date, 0, 4) == 2006) {
          if(($md < 0402) || ($md == '0402' && $hms < 020000) ||           ($md > 1029) || ($md == ‘1029’ && $hms > 020000))
               $ldate = $date . ‘-05’;
          else $ldate = $date . ‘-04’;}
     if(substr($date, 0, 4) == 2007) {
          if(($md < 0311) || ($md == '0311' && $hms < 070000) ||           ($md > 1104) || ($md == ‘1104’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(substr($date, 0, 4) == 2008) {
          if(($md < 0309) || ($md == '0309' && $hms < 070000) ||           ($md > 1102) || ($md == ‘1102’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(substr($date, 0, 4) == 2009) {
          if(($md < 0308) || ($md == '0308' && $hms < 070000) ||           ($md > 1101) || ($md == ‘1101’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(substr($date, 0, 4) == 2010) {
          if(($md < 0314) || ($md == '0314' && $hms < 070000) ||           ($md > 1107) || ($md == ‘1107’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(substr($date, 0, 4) == 2011) {
          if(($md < 0313) || ($md == '0313' && $hms < 070000) ||           ($md > 1106) || ($md == ‘1106’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(substr($date, 0, 4) == 2012) {
          if(($md < 0311) || ($md == '0311' && $hms < 070000) ||           ($md > 1104) || ($md == ‘1104’ && $hms > 070000))
               $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 18000)) . ‘-05’;
          else     $ldate = date(“Y-m-dTH:i:s”,
               (strtotime($date) – 14400)) . ‘-04’;}
     if(preg_match(“///”, $focal, $m)) {$pieces = explode(‘/’, $focal);
          $focal = intval($pieces[‘0’])/intval($pieces[‘1’]);}
     if(preg_match(“///”, $shutter, $m)) {$pieces = explode(‘/’, $shutter);
          $shutter = ‘1/’ . round($pieces[‘1’]/$pieces[‘0’]);}
     echo $shutter . ‘, ‘ . $fnum . ‘, ‘ . $focal . ‘mm, ISO’ . $iso .
          ‘, ‘ . $ldate . ‘, ‘ . $id . “n”;}

Those were tough to write. PHP’s native functions for calculating the size of files believe that a kilobyte is 1024 bytes and a megabyte is 1024*1024 bytes, which is completely false and unacceptable. I had to write my own function to calculate proper file sizes. I take all my pictures with my clock set to Greenwich Mean Time, but I still want to display the time in local time (Eastern) with the GMT offset. I couldn’t figure out how to write a generic function, so I just did it for each year up until 2012, using the United State’s Daylight Saving Time rules. I’ll have to update the function in 2013, but I hear the world is going to end in 2012 anyway.

If you think this adds to my page load time, you’re probably right. But I use W3 Total Cache to completely cache each page of my blog, so it doesn’t matter.

Next summer, I’m going to China with my Mom. I will be leaving the Eastern time zone for the first time ever. I will definitely need to update the functions above, and I will probably have to specify the time zones manually for all the photos I post from the trip. Should I worry about that now? Of course not.

Human Potential

It’s safe to say that we are the premier creature of the planet Earth. No other species has the power to influence the planet as we do. Only humans can reason, philosophize, be religious, and leave legacies.

However, there is a growing movement that wishes to relegate humans to the company of apes, sharks, and other simple creatures. Even worse, we are depicted as a plague which must be eliminated to preserve the harmony of the planet. Schoolbooks dwell on the damage we cause to the rainforests, oceans, the atmosphere, and endangered species. College professors embrace atheism, the doctrine that we are the supreme beings of the universe. Paradoxically, atheism simultaneously places humanity on a pedestal and in a pit. If there is no God, then naturally man is very important, but man is also very meaningless, because we came from the same place as ants and microbes. Thus, there is no morality or higher purpose to the human experience. You should lie, cheat, and steal if you can get away with it, and the only thing you should fear is human punishment. Obviously, this is a hedonistic belief, and all hedonistic beliefs are also limiting beliefs which stifle your potential.

Unfortunately, “personal development” as a philosophy will always be associated with the “New Age” movement. This religion (and it is a religion) recognizes neither an all-powerful God nor the absence of divinity, instead embracing a no-man’s land of spirituality without substance. Prayer is replaced with yoga. “God” is replaced with “source,” and your connection to God is your “connection to source.” The Bible is replaced with The Da Vinci Code and The Matrix trilogy. Unlike atheism, which appeals to twenty-something liberal arts students who have no income or property and mooch off their parents, the New Age movement appeals to childless women in their late 30s and early 40s, and possibly gay men. The New Age movement embraces astrology, teleology, sun-worship, witchcraft, and the worship of animals. In many ways, it’s even more pathetic than atheism.

While practicing a traditional religion such as Christianity, Hinduism, or Islam is at a higher level than atheism or faux spirituality, religion always limits your potential by answering questions that are unanswerable. How do you reconcile the Pyramids and the existence of space aliens if God created us in his image? Are we one of many experiments? Can you really know if God exists for sure? Everyone has ghosts in their lives, so you cannot deny the existence of the metaphysical realm, but the belief in an all-powerful being who pulls the strings cannot be substantiated. Therefore, the most potent humans believe that they do not know the answer. It is very difficult to live with unanswered questions, but only highly developed people can accept that some paradoxes can never be resolved.

Humans have the most potential when they leave the most doors open. If you pigeon-hole yourself into a religion, race, nationality, or belief system, you close many doors for no good reason. Adopting a multi-paradigm perspective is always better than living with a narrow mind. Our allegiance should be to the truth first. God, nation, and family come second. If you find that your family members are marauders, your nation commits genocide, and your God is Molech, do you still remain committed to them? Of course not—your reject them and find a different God, nation, and family who are not evil. Fanaticism destroys your potential, because it forbids you from negotiating with the opposition. Entrenched beliefs may be good for a civil servant, but they are the antithesis of a human being with high potential.

Are you fulfilling your human potential? What beliefs are holding you back? What people should you cut out of your life? Whom should you spend more time with? Are you living your best life now? Or are you waiting for something or someone who will never come?

Daytona State College 2010 Talent Show

My friend Farah invited me to the Daytona State College 2010 Talent Show at the college theater, bldg. 220, Thu., Oct. 7, 2010, 6-8 PM to film her dance performance. I was there for the whole show and I took 28 photos and an hour of video of all the performers.

I’ve posted them on my DaytonaState.org blog, which has not been updated in a long time: Daytona State College 2010 Talent Show: Photos & Videos.

While I didn’t play the piano like I did in 2009, and the attendance was lower, it was still a great show that I enjoyed watching.

Farah, Massiel, and Reina

Pageant contestants

Daytona State College 2010 Talent Show: Photos & Videos

Photo: Spider

Photo: Spider

An ugly spider with red spikes.

Spiny Backed Orb Weaver (Gaster Acantha)

The most colorful and easily recognized Florida spider, having what looks like a white and black spotted shell on its back, and 6 pointy red spines. The web contains tufts of silk, probably to prevent birds from flying into it.

Females are 5mm to 10mm long, 10mm to 14mm wide. They hang in the center of the web.

The bite of the Spiny Backed Orb Weaver is not known to cause serious reactions in humans.

Thanks http://www.floridaorienteering.org/nature/spiders.htm.

Fujifilm FinePix A360, 1/30, F2.8, 5.8mm, ISO100, 2005-12-20T15:42:37-05, 2005-12-20_15h42m37

Location: Thripp Residence, Ormond Beach, FL  32174-7227

Download the high-res JPEG or download the source image.

This work is licensed under a Creative Commons Attribution 3.0 License. Please credit me as “Photo by Richard Thripp” or something similar.

Photo: In the Darkness

Photo: In the Darkness

A deciduous tree in the winter at night in the moonlight.

Fujifilm FinePix A360, 1/1, F2.8, 5.8mm, ISO200, 2005-12-16T02:24:47-05, 2005-12-16_02h24m47

Location: Thripp Residence, Ormond Beach, FL  32174-7227

Download the high-res JPEG or download the source image.

This work is licensed under a Creative Commons Attribution 3.0 License. Please credit me as “Photo by Richard Thripp” or something similar.

Photo: Autumn Leaves: Flash

Photo: Autumn Leaves: Flash

Leaves on a Chinese tallow tree in autumn at night using a flash.

Fujifilm FinePix A360, 1/30, F2.8, 5.8mm, ISO100, 2005-12-15T17:46:43-05, 2005-12-15_17h46m43

Location: Thripp Residence, Ormond Beach, FL  32174-7227

Download the high-res JPEG or download the source image.

This work is licensed under a Creative Commons Attribution 3.0 License. Please credit me as “Photo by Richard Thripp” or something similar.

More of the Autumn Leaves series.