Skip to content

Instantly share code, notes, and snippets.

@jmikola

jmikola/out.txt Secret

Created August 14, 2017 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmikola/403554ac91927705ec179938d1262ad8 to your computer and use it in GitHub Desktop.
Save jmikola/403554ac91927705ec179938d1262ad8 to your computer and use it in GitHub Desktop.
UTCDateTime with timezone
object(stdClass)#1 (1) {
["date"]=>
object(LocalDateTime)#2 (2) {
["utc":"LocalDateTime":private]=>
object(MongoDB\BSON\UTCDateTime)#3 (1) {
["milliseconds"]=>
string(13) "1502739349546"
}
["tz":"LocalDateTime":private]=>
object(DateTimeZone)#4 (2) {
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/New_York"
}
}
}
object(DateTime)#5 (3) {
["date"]=>
string(26) "2017-08-14 15:35:49.546000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/New_York"
}
<?php
/* Custom document class that stores a UTCDateTime and timezone and also
* implements the UTCDateTime interface for portability. */
class LocalDateTime implements \MongoDB\BSON\Persistable, \MongoDB\BSON\UTCDateTimeInterface
{
private $utc;
private $tz;
public function __construct($milliseconds = null, \DateTimeZone $timezone = null)
{
$this->utc = new \MongoDB\BSON\UTCDateTime($milliseconds);
if ($timezone === null) {
$timezone = new \DateTimeZone(date_default_timezone_get());
}
$this->tz = $timezone;
}
public function bsonSerialize()
{
return [
'utc' => $this->utc,
'tz' => $this->tz->getName(),
];
}
public function bsonUnserialize(array $data)
{
if ( ! isset($data['utc']) || ! $data['utc'] instanceof \MongoDB\BSON\UTCDateTime) {
throw new Exception('Expected "utc" field to be a UTCDateTime');
}
if ( ! isset($data['tz']) || ! is_string($data['tz'])) {
throw new Exception('Expected "tz" field to be a string');
}
$this->utc = $data['utc'];
$this->tz = new \DateTimeZone($data['tz']);
}
public function toDateTime()
{
return $this->utc->toDateTime()->setTimezone($this->tz);
}
public function __toString()
{
return (string) $this->utc;
}
}
$bson = MongoDB\BSON\fromPHP(['date' => new LocalDateTime]);
$document = MongoDB\BSON\toPHP($bson);
var_dump($document);
var_dump($document->date->toDateTime());
@jmikola
Copy link
Author

jmikola commented Aug 14, 2017

I think this is a better way to add timezones to a UTCDateTime than using the TypeWrapper interfaces. The more I play with this while writing PHP.net examples, I'm thinking that our TypeWrapper stuff is rather half-baked. The interfaces are useful, per this gist.

For instance, we currently only support type wrappers for individual BSON classes, but a huge appeal with respect to binary would be to allow different binary subtypes to become different classes (e.g. Binary UUID types may want to work with ramsey/uuid). I think it may be a mistake to force the createFromBSONType() static factory method be in the same class/interface as the toBSONType() serialization method. createFromBSONType() can really just a be a callable passed in the $typemap array (for Cursor::setTypeMap() and the like). Then we can let it do whatever instanceof checks and logic the user may require, instead of forcing users to provide a rigid array that maps string type names to string class names.

@alcaeus
Copy link

alcaeus commented Aug 15, 2017

I agree with createFromBSONType not being forced to be in the actual type class. The method could be extracted from the type interface to a separate factory interface. If someone implements a type that is its own factory at the same time, they can just implement that method. If someone wants to create a separate factory, they'll just write a separate class and implement the factory interface there.

To avoid having to add every type to the typemap, the driver could check whether the class stored in __pclass implements the interface and only check the typemap if it doesn't. That would reduce the setup overhead (e.g. adding every custom type to the typemap) for many use cases.

@jmikola
Copy link
Author

jmikola commented Aug 15, 2017

@alcaeus: I talked this over with @derickr and I think we're going to pull the TypeWrapper interface out of 1.3.0, but leave the various BSON type interfaces (e.g. BinaryInterface). As noted in my example above, they can be useful for abstraction purposes.

It occurred to me that both type wrapping (PHPC-640) and the field path syntax (PHPC-314) may be better handled by letting users provide a callable that receives the original BSON value and the field path as a string. That approach let's us reduce much of the complexity in the BSON-to-PHP decoding within bson.c and it allows users the most flexibility. For instance, PHPLIB could have its own callable that applies any logic we might want and then invokes another callable from the library user (akin to stacking autoloaders). Benchmarking would help us determine if this is feasible, but I think it may be comparable to how we're currently calling createFromBSONType() frequently (granted, the callable could be invoked many more times for large documents).

In concert with removing createFromBSONType() entirely, I'm also going to look at doing away with the TypeWrapper interface. @derickr and I don't recall the exact reasons that we originally decided not to use MongoDB\BSON\Serializable for this purpose, but I'd like to explore relaxing its restriction on the bsonSerialize() return type. Docs currently state that it must return an array or object, which limits it to producing BSON arrays and documents. If we instead let it return any PHP value (including a BSON type object or PHP primitive), it may be able to take the place of MongoDB\BSON\TypeWrapper::toBSONType(). Of course, we'd have a sanity check to ensure that any bsonSerialize() return value destined to become a root document is actually an array or object.

For MongoDB\BSON\Unserializable, I expect bsonUnserialize() stays the same, which is to say it's only used to convert BSON arrays and documents into new PHP objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment