Travelling through Date and Time, part IV

Welcome to the fourth installment of my series about date and time in PHP. Let’s review what we’ve done so far: In the first part, we looked at the basics (DateTime object and friends). Then, we applied the theory and discussed the basic outline for an application in FLOW3. Finally we dealt with the until-then omitted timezones. Still, there is one important thing that is missing. It could be summarized by the following question: What does 07/09/08 mean? Well, yes, it’s a date, but it could be a very different date depending how you interpret it. Personally, I’m always confused when I come across a date formatted differently to what I’m used to … Which leads us to today’s topic: We need localization in order to make it easy for everyone to understand the dates in our application.

How do we do this? Unfortunately, this time it’s not that clear. Basically, you have two options to consider: strftime and IntlDateFormatter. We’ll have a look at both and will see advantages and disadvantages of both approaches. But, first and foremost, they have one thing in common: They don’t work with DateTime. What the … ? Yes. But at least they both work with timestamps ;)

Let’s start with IntlDateFormatter. This class was introduced in PHP 5.3 and is only available if the PECL extension intl is installed. To use it, you need to define a format object first, like this:
$enUSFormat = new IntlDateFormatter('en_US', IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/New_York', IntlDateFormatter::GREGORIAN);
To find out more about the options, please refer to the docs.
Finally, you can use this format object to get a localized output of a timestamp like so:
$enUSFormat->format($myDateTime->getTimestamp());
If you want to have more/full control about the format used, you can also specify a pattern at the end:
$enUSFormat = new IntlDateFormatter('en_US', IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/New_York', IntlDateFormatter::GREGORIAN, 'MM/dd/yyyy');
Possible patterns can be found on the ICU website.

The other option is to go with strftime. It’s the traditional way of doing things, and, in contrast to IntlDateFormatter, it is locale aware. That means that the output depends on the locale setting, which can be changed like this:
setlocale(LC_TIME, 'en_US');
Note that either LC_ALL (locale setting for all localization areas) or LC_TIME (only for date/time formatting) works fine here.
Then, you can use strftime like this:
strftime('%c', $myDateTime->getTimestamp());
%c is just one of the formatting options you have. There are a lot more available which offer possibilities similar to IntlDateFormatter’s pattern string. Again, for more information, please refer to the docs.

To conclude, which option should be used? I’d opt for strftime. Mainly because it is available on all PHP setups (a big point in my opinion), and also because it is easier to use in conjunction with setlocale. The disadvantage of strftime is that it uses a different format string than date() or DateTime::format() in contrast to IntlDateFormatter.

With this, I’m going to end my journey through date and time in PHP. I hope you enjoyed it!

filed under , & posted on April 24, 2010

Travelling through Date and Time, part III

Until now, we looked at the basics and started to work with our dates. But we didn’t deal with timezones, which means all we did was pretty useless. In this part, we’re going to change that.

Okay, so what exactly are timezones? According to Wikipedia, a timezone “is a region of the earth that has uniform standard time, usually referred to as the local time”. And “local time” could be defined like this: “local time is UTC plus the current timezone offset for the considered location”. Note that it says “current offset”, not just “offset”, because a location can have different offsets at different times (e.g. because of daylight savings time). By the way: UTC stands for “Coordinated Universal Time” (actually: “Universal Time, Coordinated”) and is roughly equivalent to the formerly used GMT (Greenwich Mean Time). It is considered to be the “world time”, on which basis all other timezones are calculated. More on this topic can be found on Wikipedia, but I think this will do for our task ;)

In part I and II we completely omitted the fact that we live in timezones. Let’s have a closer look at what happened when we didn’t specify a timezone along with our dates. Or did we? Since PHP 5.3, there is a php.ini setting for the default timezone, e.g.: date.timezone = 'Europe/Berlin'. Actually, if you haven’t set that and are using something like date(), you’ll get a warning. But in case this is set, all DateTime objects without any specific timezone are implicit in the timezone set in the php.ini. So, in my setup, my dates would have been in Europe/Berlin, but the very same script could produce completely different results in your setup. Which could cause problems.

But it’s not all bad. Remember the definiton in the beginning? Every local time is computed by UTC plus current offset. So all things are in relation to UTC. And the unix timestamp, which we got to know in the first part of this series, is always in UTC. This proves really helpful. Let’s see why:
Say we create a DateTime object, this time with a specific timezone, like this:
$dateInBerlin = new DateTime('2010-04-20 10:00:00', new DateTimeZone('Europe/Berlin'));
And another date:
$dateInMoscow = new DateTime('2010-04-20 12:00:00', new DateTimeZone('Europe/Moscow'));
On April 20th, the timezone offset from Berlin to UTC is 2 hours, and from Moscow to UTC is 4 hours, so the difference between Berlin and Moscow timezone is 2 hours. Note that our dates are not exactly the same: One is at 10:00, the other at 12:00. Now, if we call getTimestamp() on both objects, we’ll get the same timestamp (1271750400) for both dates. Why? Because the timestamp consists of seconds elapsed since January 1st 1970 in UTC timezone. And because of the time difference between Berlin and Moscow, the dates are the same when expressed in UTC time.

OK, what does that mean for us? As we already know, FLOW3 stores the DateTime objects in the database as unix timestamps. To convert the DateTime object to an integer, FLOW3 uses the getTimestamp() method. Therefore, whatever timezone our date is in, we’ll get the same timestamp. What happens on reconstitution? The timestamp in the database is set via setTimestamp(). At this point, we’ll have a DateTime which value is the timestamp (UTC) and which expresses this in the default timezone specified in the php.ini.
But because I want to store a timezone along with my events, we need to do something before displaying them. We need to make sure that format() uses my timezone and not the default timezone. It turns out this is actually quite easy to do. DateTime objects have the method setTimezone(), which expects a valid timezone identifier as an argument. It then modifies the timezone, but not the timestamp. This is very important, as format() will output something different now, but getTimestamp() will still be the same. Mission accomplished.

Which means all is well. Or is it? No, not quite. Because if we want to associate a timezone with an event, we’ll probably have a form which allows the input of a date, along with a dropdown of possible timezones. Of course, the user enters the date corresponding to the timezone he selected. So before saving the date, we have to convert the entered date to express the correct timestamp (which is: an UTC expression of the combination of entered date and selected timezone). Note that just setting the timezone via setTimezone() doesn’t work here, because we actually want to modify the timestamp inside the DateTime object. Of course, this conversion has only to take place if we create the DateTime object before we have the timezone information available. On the other hand, if we change the timezone after we set the date, we have to modify the date again.

The last bit shows that timezone handling can get a bit complicated, but what it comes down to is basically to make sure that all dates in the database are in UTC. Once that is ensured, we’re safe. Additionaly, we also solved the problem of different php.ini settings, as our solution is now independent of this setting.

Next up: Localization.

filed under , , & posted on April 22, 2010

Travelling through Date and Time, part II

Now that we covered the basices in part I, let’s actually do something. I’ll explain how to fill the model with data and how to get specific models from the repository in FLOW3.

Before we begin, let’s create some classes: A DemoController which will showcase some of our calendar functionality. Then, we’ll need an AdminController with basic CRUD actions and corresponding Fluid templates. For the Domain, I’m going to use an Event model and an EventRepository. To make it simple, each event will only contain a start date (startDateTime), an end date (endDateTime) and a title (title) and the respective getters and setters.

Let’s start with the AdminController. The question that arises is: How to deal with our DateTime objects? Obviously, we can’t just display a DateTime object in a form textfield, and on the other end, we’ll receive a string from the form instead of a DateTime object. It would be easy if we could get and set a string for the date and also the time (I think it’s good to have two separate textfields for date and time). To be able to do this, let’s create the needed getters and setters: getStartDate/getEndDate and getStartTime/getEndTime. How do they work? The get*-methods return a formatted version of the DateTime objects (e.g. $this->startDateTime->format('Y-m-d');), the set*-methods receive a string from the form and call setDate/setTime on the DateTime objects (e.g. $this->startDateTime->setDate($year, $month, $day); – of course, the variables have to be extracted from the string). For all of this to work, we have to make sure that the DateTime objects are never null, so we have to initialize them in the constructor.

Problem solved, so now on to the repository. Here we need to create our own methods which return subsets of events. This is actually a bit tricky and I won’t go into much detail here. But let’s have a look at how it is supposed to work generally. I would suggest having one method which can return all events between a beginning and an end (of course, it should work with no beginning or no end as well). We’ll call it getBetween. This method would be called by several wrappers which themselves are easier to use in a controller. E.g., it would be handy to have getIn (which allows us to specify only, for example, a year and the method determines beginning and end automatically), getLatest (skipping beginning and setting end to the current date) and getUpcoming (skipping end and using the current date as end).

And how does getBetween work? Or more to the point, how does it compare the dates? As we’ve seen in part I, FLOW3 stores all DateTime objects as unix timestamps in the database, which means our method getBetween has to create timestamps from the given arguments beginning and end, which then can be compared to the values in the database.

filed under , , & posted on April 20, 2010

Travelling through Date and Time, part I

This is the first part of a series (of yet-to-be-determined length) dealing with date and time in PHP (and FLOW3).

This first part will cover the very basics of this complex and often very weird topic. Please note that what you’ll read here is often not the whole story, and will be completed in the next parts.

So why am I doing this? Well, I’m trying to put together a calendar package for FLOW3. The calendar should be able to handle events which have a beginning and an optional end. A beginning consists of a date (year, month and day) and, again optional, a time (hour and minute, no seconds). The same applies for the end of an event. Furthermore, when creating an event, the admin should be able to specify the timezone of the dates entered (one timezone for both dates).

This blog post will only deal with the handling of date and time in PHP and FLOW3, I’ll leave the timezone handling for another time.
So, what do we have? Basically, when using date and time in PHP, we use DateTime. DateTime was introduced in PHP 5.2 and provides a way of dealing with, well, date and time. Unfortunately, DateTime is a bit weird. For example, there are at least 3 options to set the date and time of a DateTime objects.
One is to hand it a UNIX timestamp (since 5.3, via setTimestamp). This is the number of elapsed seconds since January 1st 1970, 00:00. In PHP, an integer is used for timestamps. On 32-bit systems, this will mean we’ll run into problems in 2038 because too many seconds will have passed by then to be hold in an 32-bit signed integer. Of course, probably all systems will be 64-bit by then, so not to worry on the PHP part here in my opinion.
A second option is to create the DateTime object with a string like 2010-04-15 17:06:02. You can use any string that you could also pass to strtotime. When you create a DateTime object with no arguments, the string "now" is assumed and you’ll get an object which represents the current date and time.
Weird enough, there is no method similar to setTimestamp to do the same thing later on an object, you have to use the static method DateTime::createFromFormat or use something like the add/sub methods to change the DateTime object later on …
Thirdly, you can use setDate and setTime to modify parts of a DateTime object.

Ok, now on to the next topic: Gettting the date and time back from the object. format() exists for this purpose, which takes a format string (with the same options as for date()). Unfortunately, there is no locale aware format method, but again, more on this in another blog post.

With this covered, what about FLOW3? Basically, handling DateTime objects is baked right into the persistence layer. But how is it persisted? Well, let’s take a step back and have a look at MySQL for a second.
There, you have several options: You can store a regular INTEGER (unix timestamp), or a DATETIME value (and some other options as well). The latter stores a STRING representation of a date and time, but has the advantage that you can do several operations in SQL like comparisons. The INTEGER version of course has the disadvantage that it could run into the 2038 problem mentiond earlier.
Now, back to FLOW3: The persistence backend is generating all SQL queries from the query object, so we can’t use the date/time comparisons. Also, some of these things are MySQL only anyway, so usage of them is not an option for FLOW3 which aims to be more or less backend independent. That means FLOW3 turns the DateTime object into a unix timestamp before persisting it (in an INTEGER field), and converts it back upon reconstitution.

Enough for today. The plan is to write more about handling and comparing DateTime values in FLOW3 in the next part. The third part will probably deal with timezones and then, we’ll have a look at localization.

filed under , , & posted on April 15, 2010

PHP Environement with MacPorts

So I was happy with my native PHP setup.
Until I updated Snow Leopard to 10.6.3.
The problem with the update is that it also updates PHP from 5.3.0 to 5.3.1. This in itself would be fine, but it also enables zend-multibyte. And this causes problems with FLOW3, because FLOW3 uses declare(ENCODING = 'utf-8'); at the beginning of every file, which throws an error on 5.3.1 with enabled zend-multibyte because of a bug (see the FLOW3 mailing list for details). This bug is fixed in PHP 5.3.2, but Snow Leopard 10.6.3 includes only 5.3.1.

Still with me? Okay, let’s fix this mess. How? If we were to fix this in the default setup, we’re going to run into these kind of problems again and again. To me, the only reasonable solution is to have your own operating-system independent solution.

So here’s what I did (and you should do, too):
Start by installing MacPorts if you don’t have it already. Then, install the newest Apache and PHP with the following commands:

sudo port install apache2
sudo port install php5 +apache2 +pear

You’ll also need the PDO drivers and the mbstring extension, get them with:

sudo port install php5-sqlite
sudo port install php5-mysql
sudo port install php5-mbstring

And then you probably have to adjust the settings. Now, MacPorts installs everything under /opt/local, so that’s were you should be looking for the files:

hosts is still at /etc/hosts (of course)
httpd.conf is at /opt/local/apache2/conf/httpd.conf
httpd-vhosts.conf is at
/opt/local/apache2/conf/extra/httpd-vhosts.conf
[username].conf is at
/opt/local/apache2/conf/extra/[username].conf

Some important things to check:

  • Are [username].conf and httpd-vhosts.conf included in your httpd.conf? (Just do a quick search on both)
  • Does httpd.conf handle php files? If not, enable it at the end of the file with
    <IfModule php5_module>
    AddType application/x-httpd-php .php
    </IfModule>
    
  • Is overriding allowed? Search for <Directory /> and ensure
    AllowOverride is set to All

That’s it, restart Apache

sudo /opt/local/etc/LaunchDaemons/ »
org.macports.apache2/apache2.wrapper stop
sudo /opt/local/etc/LaunchDaemons/ »
org.macports.apache2/apache2.wrapper start

And of course you don’t want to do this everytime, so have it start on launch with:

sudo launchctl load -w »
/Library/LaunchDaemons/org.macports.apache2.plist

Hope this helps … if you have any questions or corrections, please let me know.

filed under , , & posted on April 5, 2010

Review: Crockford on JavaScript

As I mentioned in my blog post “Learning JavaScript” a couple of days ago, I decided to watch the series “Crockford on JavaScript”. And I did watch them!
So far, I listened to part 2-4 and will definitely watch part 5 once it’s out. Although the talks are a bit lengthy at times, I would say I learned more about JavaScript than in the last 2 years. And I also learned a couple of things I didn’t expect (like a little bit of background about how HTML came to be).
In this post, I want to share some points I noted down while watching:

  • I’m still unsure if the object model of JavaScript is really suited for large programs, I think I’m still favoring a classical model like one in Java or PHP. But more important is that I understand the “JavaScript way” of doing things (e.g. prototypal inheritance) better, because using a language as it was intended to be used will yield better results.
  • I fully agree with Crockfords position that numbers should be numbers in programming languages and not ints, floats, doubles and what-not. Wish that PHP would be like this.
  • Functions are really good in JavaScript, and I finally got what closures are all about. The whole concept sounds really nice, so, yes, JavaScript has good parts :)
  • Getting the introduction to DOM that Crockford gave was great to fill in gaps and to deepen my understanding on why things are how they are.
  • And finally, it was interesting to see what he thinks about HTML, CSS, DOM and the W3C. Pretty strong opinions here, but I think he is right to a certain extent. The standards we have today have a mostly crazy history and do several things wrong, but we have to use them. Unfortunately, the output of the groups like the W3C is much worse than what we get from Ajax libraries (or any other framework we have). Of course, this is not taking into account that you can just ditch a library, but you can’t work around JavaScript. And this is also why I disagree with Crockford that the good parts saved JavaScript, I would say JavaScript survived, because it is the only thing that all browsers support.

So that was my quick review of the talks. I highly recommend taking the time to watch them if you are interested in the web and want to understand things on a deeper level.

filed under , & posted on March 24, 2010

My TYPO3 default template

After setting up several TYPO3 websites, I thought it was time to build my own default template to start with for new projects. I’m sharing this with everyone in the hope it is useful, but be aware that the template is only my current way of doing things, and will probably change in the future. Also, I do not recommend to copy-and-paste it if you do not know what all these lines do. Look them up in the TSref.

######### PAGE #########
page = PAGE
page {
 typeNum = 0
 meta {
  language = de
  description = TYPO3TEST
  keywords =
 }
 includeCSS {
  file1 = {$css_file1}
 }
 includeJS {

 }
 shortcutIcon = {$favicon_file}
}

######### CONFIG #########
config {
 doctype = xhtml_strict
 xmlprologue = none
 xhtml_cleaning = all
 disablePrefixComment = 1
 removeDefaultJS = 1
}
lib {
 parseFunc_RTE.nonTypoTagStdWrap.encapsLines.addAttributes.P.class >
 stdheader.stdWrap.dataWrap >
 stdheader.3.headerClass >
}
tt_content {
 stdWrap.innerWrap.cObject.default {
  10 >
  15 >
  20 >
  30.value = |
 }
 stdWrap.dataWrap >
}
plugin {
 tx_cssstyledcontent._CSS_DEFAULT_STYLE >
}

######### MENU #########
lib.navigation = HMENU
lib.navigation {
 1 = TMENU
 1 {
  wrap = <ul>|</ul>
  expAll = 1
  NO {
   wrapItemAndSub = <li>|</li>
  }
  ACT = 1
  ACT {
   wrapItemAndSub = <li class="active">|</li>
  }
 }
 2 = TMENU
 2 {
  wrap = <ul>|</ul>
  NO {
   wrapItemAndSub = <li>|</li>
  }
  ACT = 1
  ACT {
   wrapItemAndSub = <li class="active">|</li>
  }
 }
}

######### REALURL #########
config {
 simulateStaticDocuments = 0
 baseURL = my.domain.tld
 tx_realurl_enable = 1
}

######### TEMPLATE #########
page.10 = USER
page.10.userFunc = tx_templavoila_pi1->main_page

filed under , & posted on March 16, 2010

Learning JavaScript

Okay, I give in. I’m going to learn JavaScript now.

Once again I realised I’m spending a lot of time of a project (and it’s only getting more and more) on the JavaScript part and don’t feel very comfortable with it. Yes, I’ve used JavaScript (more to the point, jQuery) for many years now, but I use it like PHP without the dollar sign. Which sucks ¬†…

So, what am I going to do?

First, I’m going to listen to Douglas Crockford:

Second, I’ll have a look at another JavaScript framework: ExtJS. I chose ExtJS because it is focused more on web applications than jQuery (which I still love!), and TYPO3 v5 will use it. I haven’t decided yet which book to read, but am determined to pick one after I got the basics right.

filed under , & posted on March 14, 2010

FLOW3: Updating entities inside an aggregate

The manual explains how to update entities that are accessible via a repository, but it does not say explicitly how to update an entity within an aggregate. In this post, I’ll show which steps need to be taken to do that.

Assume you have the following to models: Foo and Bar, and Foo contains an SplObjectStorage of Bars. Foo is the aggregate root and has its own FooRepository. Bar should not be accessible from the outside but only through traversal via Foo. Now, how to update a Bar object? Obviously, you can’t do $this->barRepository->update($bar); because there is no bar repository!

So, let’s start by looking at the controller and the views. Create basic index, new, create, edit and save actions. All of these need a $foo as an argument (additonally to the $bar that you want to create/edit/update). So you need to give all links (or forms) in the views¬†arguments={foo: foo}.

The controller then needs to set the < code >$bar in the $foo. When creating, this could be $foo->addBar($bar).
For updating, I suggest calling $foo->updateBar($bar).

Of course you need to implement these to methods in the model Foo. addBar() is easy, just attach the given $bar to the SplObjectStorage of Bars. updateBar() is a little more complicated, because you first need to find out which of the items in the SplObjectStorage needs to be replaced. This can be done with the following code:

$editedBarIdentifier = »
$this->persistenceManager->getIdentifierByObject($editedBar);
$originalBar =  $this->persistenceManager-> »
getObjectByIdentifier($editedBarIdentifier);
$this->bars->detach($originalBar);
$this->bars->attach($editedBar);

As you can see, that requires a persistence manager object, which can be injec ted via dependency injection in the model. Make sure to set the @transient annotation to tell the persistence framework that you don't want it to track the persistence manager object (of course!).

That's it. Happy updating!

filed under , & posted on March 8, 2010

Programmatic ViewHelpers for FLOW3

In the current version of FLOW3 (1.0.0 alpha7), there is no way to create Fluid ViewHelpers programmatically. But there is a workaround which is not that bad either. I got the inspiration from the admin package, so all kudos goes to the creator ;)

First off, why would you want something like this? I wanted to accomplish the following: I have a page entity which contains several page items. Every page item shares the same interface, e.g. all have a display method. Of course I don’t want to generate HTML code directly in there, but have Fluid ViewHelpers based on the properties of the page item.

The soultion is rather simple: Instead of generating Fluid ViewHelpers in the code, render a view! So for every page item, create a Fluid template, and the display method of the page item makes sure all necessary view variables are set.

Here is the code needed in the page item’s display method:

$view = $objectFactory->create('F3\Fluid\View\TemplateView');
$view->setControllerContext($controllerContext);
$view->setTemplatePathAndFilename('package:// »
Your-Package-Name/Private/Templates/PageItemName.html');
$view->assign('varName', $var);
return $view->render();

That’s it. Of course, you need to make sure you have an objectFactory at that place, as well as the controllerContext which you can get from the current controller via getControllerContext().

filed under , & posted on March 6, 2010
Next Page »