Cathedral Antwerp July 2015-1

Oh, PHP. It's the butt of any number of jokes in the programming community. Those who do PHP often lie and pretend they don't, just to avoid the social stigma. Today's submitter not only works in PHP, but they also freelance: the bottom of the bottom of the development hierarchy.

Last year, Ilya was working on a Joomla upgrade as well as adjusting several components on a big, obscure website. As he was poking around in the custom code, he found today's submission. You see, the website is in Italian. At the top of the page, it shows not only the date, but also the saint of the day. This is a Catholic thing: every day of the year has a patron saint, and in certain cultures, you might even be named for the saint whose day you were born on. A full list can be found on this Italian Wikipedia page.

Every day, the website was supposed to display text like "18 luglio: santi Sinforosa e sette compagni" (July 18: Sinforosa and the Seven Companions). But the code that generated this string had broken. It wasn't Ilya's task to fix it, but he chose to do so anyway, because why not?

His first suspect for where this text came from was this mess of Javascript embedded in the head:

     function getDataGiorno(){
     data = new Date();
     ora =data.getHours();
     minuti=data.getMinutes();
     secondi=data.getSeconds();
     giorno = data.getDay();
     mese = data.getMonth();
     date= data.getDate();
     year= data.getYear();
     if(minuti< 10)minuti="0"+minuti;
     if(secondi< 10)secondi="0"+secondi;
     if(year<1900)year=year+1900;
     if(ora<10)ora="0"+ora;
     if(giorno == 0) giorno = " Domenica ";
     if(giorno == 1) giorno = " Lunedì ";
     if(giorno == 2) giorno = " Martedì ";
     if(giorno == 3) giorno = " Mercoledì ";
     if(giorno == 4) giorno = " Giovedì ";
     if(giorno == 5) giorno = " Venerdì ";
     if(giorno == 6) giorno = " Sabato ";
     if(mese == 0) mese = "gennaio ";
     if(mese ==1) mese = "febbraio ";
     if(mese ==2) mese = "marzo ";
     if(mese ==3) mese = "aprile ";
     if(mese ==4) mese = "maggio ";
     if(mese ==5) mese = "giugno ";
     if(mese ==6) mese = "luglio ";
     if(mese ==7) mese = "agosto ";
     if(mese ==8) mese = "settembre ";
     if(mese ==9) mese = "ottobre ";
     if(mese ==10) mese = "novembre ";
     if(mese ==11) mese = "dicembre";
     var dt=date+" "+mese+" "+year;
     var gm =date+"_"+mese;

     return gm.replace(/^\s+|\s+$/g,""); ;
     }

     function getXMLHttp() {
     var xmlhttp = null;
     if (window.ActiveXObject) {
       if (navigator.userAgent.toLowerCase().indexOf("msie 5") != -1) {
         xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
       } else {
           xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
       }
     }
     if (!xmlhttp && typeof(XMLHttpRequest) != 'undefined') {
       xmlhttp = new XMLHttpRequest()
     }
     return xmlhttp
     }

     function elaboraRisposta() {
      var dt=getDataGiorno();
      var data = dt.replace('_',' ');
      if (dt.indexOf('1_')==0){
          dt.replace('1_','%C2%BA');
      }
       // alert("*"+dt+"*");
     var temp = new Array();
     temp = objHTTP.responseText.split(dt);
     //alert(temp[1]);

     var temp1=new Array();
     temp1=temp[1].split(":");
     temp=temp1[1].split("");


      if (objHTTP.readyState == 4) {
      santi=temp[0].split(",");
      //var app = new Array();
      //app=santi[0].split(";");
      santo=santi[0];
      //alert(santo);

        // document.write(data+" - "+santo.replace(/\/wiki\//g,"http://it.wikipedia.org/wiki/"));
        document.write(data+" - "+santo);
      }else {

      }

     }
     function loadDati() {
      objHTTP = getXMLHttp();
      objHTTP.open("GET", "calendario.html" , false);

      objHTTP.onreadystatechange = function() {elaboraRisposta()}


     objHTTP.send(null)
     }

If you've never seen Joomla before, do note that most templates use jQuery. There's no need to use ActiveXObject here.

"calendario.html" contained very familiar text: a saved copy of the Wikipedia page linked above. This ought to be splitting the text with the date, avoiding parsing HTML with regex by using String.prototype.split(), and then parsing the HTML to get the saint for that date to inject into the HTML.

But what if a new saint gets canonized, and the calendar changes? By caching a local copy, you ensure that the calendar will get out of date unless meticulously maintained. Therefore, the code to call this Javascript was commented out entirely in the including page:

<div class="santoForm">
<!--  <?php echo "<script>loadDati()</script>" ;  ?> -->
<?php ...

Instead, it had been replaced with this:

       setlocale(LC_TIME, 'ita', 'it_IT.utf8');
       $gg = ltrim(strftime("%d"), '0');

       if ($gg=='1'){
        $dt=ltrim(strftime("1º %B"), '0');
       }else{
         $dt=ltrim(strftime("%d %B"), '0');
       }
       //$dt='4 febbraio';
     $html = file_get_contents('http://it.wikipedia.org/wiki/Calendario_dei_santi');
     $doc = new DOMDocument();
     $doc->loadHTML($html);
     $elements = $doc->getElementsByTagName('li');
     foreach ($elements as $element) {
        if (strpos($element->nodeValue,utf8_encode($dt))!== false){
         list($santo,$after)= split(';',$element->nodeValue);
         break ;
        }else {}
     }
       list($santo,$after)= split(',',utf8_decode($santo));
       if (strlen ( $santo) > 55) {
         $santo=substr($santo, 0, 55)."...";
       }

This migrates the logic to the backend—the One True Place for all such logic—and uses standard library routines, just as it should. Of course, this being PHP, it breaks horribly if you look at it cross-eyed, or—like Ilya—don't have the Italian locale installed on your development machine. And of course, it'll also break if the live Wikipedia page about the saints gets reformatted. But what's the likelihood of that? Plus, it's not cached in the least, letting every visitor see updates in real time. After all, next time they canonize a saint, everyone will rush right to this site to verify that the day changed. That's how the Internet works, right?

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!