Projet

Général

Profil

Paste
Télécharger (22,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / feeds / plugins / FeedsParser.inc @ 74f6bef0

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains FeedsParser and related classes.
6
 */
7

    
8
/**
9
 * A result of a parsing stage.
10
 */
11
class FeedsParserResult extends FeedsResult {
12
  public $title;
13
  public $description;
14
  public $link;
15
  public $items;
16
  public $current_item;
17

    
18
  /**
19
   * Constructor.
20
   */
21
  public function __construct($items = array()) {
22
    $this->title = '';
23
    $this->description = '';
24
    $this->link = '';
25
    $this->items = $items;
26
  }
27

    
28
  /**
29
   * @todo Move to a nextItem() based approach, not consuming the item array.
30
   *   Can only be done once we don't cache the entire batch object between page
31
   *   loads for batching anymore.
32
   *
33
   * @return
34
   *   Next available item or NULL if there is none. Every returned item is
35
   *   removed from the internal array.
36
   */
37
  public function shiftItem() {
38
    $this->current_item = array_shift($this->items);
39
    return $this->current_item;
40
  }
41

    
42
  /**
43
   * @return
44
   *   Current result item.
45
   */
46
  public function currentItem() {
47
    return empty($this->current_item) ? NULL : $this->current_item;
48
  }
49
}
50

    
51
/**
52
 * Abstract class, defines interface for parsers.
53
 */
54
abstract class FeedsParser extends FeedsPlugin {
55

    
56
  /**
57
   * Implements FeedsPlugin::pluginType().
58
   */
59
  public function pluginType() {
60
    return 'parser';
61
  }
62

    
63
  /**
64
   * Parse content fetched by fetcher.
65
   *
66
   * Extending classes must implement this method.
67
   *
68
   * @param FeedsSource $source
69
   *   Source information.
70
   * @param $fetcher_result
71
   *   FeedsFetcherResult returned by fetcher.
72
   */
73
  public abstract function parse(FeedsSource $source, FeedsFetcherResult $fetcher_result);
74

    
75
  /**
76
   * Clear all caches for results for given source.
77
   *
78
   * @param FeedsSource $source
79
   *   Source information for this expiry. Implementers can choose to only clear
80
   *   caches pertaining to this source.
81
   */
82
  public function clear(FeedsSource $source) {}
83

    
84
  /**
85
   * Declare the possible mapping sources that this parser produces.
86
   *
87
   * @ingroup mappingapi
88
   *
89
   * @return
90
   *   An array of mapping sources, or FALSE if the sources can be defined by
91
   *   typing a value in a text field.
92
   *
93
   *   Example:
94
   *   @code
95
   *   array(
96
   *     'title' => t('Title'),
97
   *     'created' => t('Published date'),
98
   *     'url' => t('Feed item URL'),
99
   *     'guid' => t('Feed item GUID'),
100
   *   )
101
   *   @endcode
102
   */
103
  public function getMappingSources() {
104
    self::loadMappers();
105
    $sources = array();
106
    $content_type = feeds_importer($this->id)->config['content_type'];
107
    drupal_alter('feeds_parser_sources', $sources, $content_type);
108
    if (!feeds_importer($this->id)->config['content_type']) {
109
      return $sources;
110
    }
111
    $sources['parent:uid'] = array(
112
      'name' => t('Feed node: User ID'),
113
      'description' => t('The feed node author uid.'),
114
    );
115
    $sources['parent:nid'] = array(
116
      'name' => t('Feed node: Node ID'),
117
      'description' => t('The feed node nid.'),
118
    );
119
    return $sources;
120
  }
121

    
122
  /**
123
   * Get an element identified by $element_key of the given item.
124
   * The element key corresponds to the values in the array returned by
125
   * FeedsParser::getMappingSources().
126
   *
127
   * This method is invoked from FeedsProcessor::map() when a concrete item is
128
   * processed.
129
   *
130
   * @ingroup mappingapi
131
   *
132
   * @param $batch
133
   *   FeedsImportBatch object containing the sources to be mapped from.
134
   * @param $element_key
135
   *   The key identifying the element that should be retrieved from $source
136
   *
137
   * @return
138
   *   The source element from $item identified by $element_key.
139
   *
140
   * @see FeedsProcessor::map()
141
   * @see FeedsCSVParser::getSourceElement()
142
   */
143
  public function getSourceElement(FeedsSource $source, FeedsParserResult $result, $element_key) {
144

    
145
    switch ($element_key) {
146

    
147
      case 'parent:uid':
148
        if ($source->feed_nid && $node = node_load($source->feed_nid)) {
149
          return $node->uid;
150
        }
151
        break;
152
      case 'parent:nid':
153
        return $source->feed_nid;
154
    }
155

    
156
    $item = $result->currentItem();
157
    return isset($item[$element_key]) ? $item[$element_key] : '';
158
  }
159
}
160

    
161
/**
162
 * Defines an element of a parsed result. Such an element can be a simple type,
163
 * a complex type (derived from FeedsElement) or an array of either.
164
 *
165
 * @see FeedsEnclosure
166
 */
167
class FeedsElement {
168
  // The standard value of this element. This value can contain be a simple type,
169
  // a FeedsElement or an array of either.
170
  protected $value;
171

    
172
  /**
173
   * Constructor.
174
   */
175
  public function __construct($value) {
176
    $this->value = $value;
177
  }
178

    
179
  /**
180
   * @todo Make value public and deprecate use of getValue().
181
   *
182
   * @return
183
   *   Value of this FeedsElement represented as a scalar.
184
   */
185
  public function getValue() {
186
    return $this->value;
187
  }
188

    
189
  /**
190
   * Magic method __toString() for printing and string conversion of this
191
   * object.
192
   *
193
   * @return
194
   *   A string representation of this element.
195
   */
196
  public function __toString() {
197
    if (is_array($this->value)) {
198
      return 'Array';
199
    }
200
    if (is_object($this->value)) {
201
      return 'Object';
202
    }
203
    return (string) $this->getValue();
204
  }
205
}
206

    
207
/**
208
 * Encapsulates a taxonomy style term object.
209
 *
210
 * Objects of this class can be turned into a taxonomy term style arrays by
211
 * casting them.
212
 *
213
 * @code
214
 *   $term_object = new FeedsTermElement($term_array);
215
 *   $term_array = (array)$term_object;
216
 * @endcode
217
 */
218
class FeedsTermElement extends FeedsElement {
219
  public $tid, $vid, $name;
220

    
221
  /**
222
   * @param $term
223
   *   An array or a stdClass object that is a Drupal taxonomy term.
224
   */
225
  public function __construct($term) {
226
    if (is_array($term)) {
227
      parent::__construct($term['name']);
228
      foreach ($this as $key => $value) {
229
        $this->$key = isset($term[$key]) ? $term[$key] : NULL;
230
      }
231
    }
232
    elseif (is_object($term)) {
233
      parent::__construct($term->name);
234
      foreach ($this as $key => $value) {
235
        $this->$key = isset($term->$key) ? $term->$key : NULL;
236
      }
237
    }
238
  }
239

    
240
  /**
241
   * Use $name as $value.
242
   */
243
  public function getValue() {
244
    return $this->name;
245
  }
246
}
247

    
248
/**
249
 * A geo term element.
250
 */
251
class FeedsGeoTermElement extends FeedsTermElement {
252
  public $lat, $lon, $bound_top, $bound_right, $bound_bottom, $bound_left, $geometry;
253
  /**
254
   * @param $term
255
   *   An array or a stdClass object that is a Drupal taxonomy term. Can include
256
   *   geo extensions.
257
   */
258
  public function __construct($term) {
259
    parent::__construct($term);
260
  }
261
}
262

    
263
/**
264
 * Enclosure element, can be part of the result array.
265
 */
266
class FeedsEnclosure extends FeedsElement {
267
  protected $mime_type;
268

    
269
  /**
270
   * Constructor, requires MIME type.
271
   *
272
   * @param $value
273
   *   A path to a local file or a URL to a remote document.
274
   * @param $mimetype
275
   *   The mime type of the resource.
276
   */
277
  public function __construct($value, $mime_type) {
278
    parent::__construct($value);
279
    $this->mime_type = $mime_type;
280
  }
281

    
282
  /**
283
   * @return
284
   *   MIME type of return value of getValue().
285
   */
286
  public function getMIMEType() {
287
    return $this->mime_type;
288
  }
289

    
290
  /**
291
   * Use this method instead of FeedsElement::getValue() when fetching the file
292
   * from the URL.
293
   *
294
   * @return
295
   *   Value with encoded space characters to safely fetch the file from the URL.
296
   *
297
   * @see FeedsElement::getValue()
298
   */
299
  public function getUrlEncodedValue() {
300
    return str_replace(' ', '%20', $this->getValue());
301
  }
302

    
303
  /**
304
   * Use this method instead of FeedsElement::getValue() to get the file name
305
   * transformed for better local saving (underscores instead of spaces)
306
   *
307
   * @return
308
   *   Value with space characters changed to underscores.
309
   *
310
   * @see FeedsElement::getValue()
311
   */
312
  public function getLocalValue() {
313
    return str_replace(' ', '_', $this->getValue());
314
  }
315

    
316
  /**
317
   * @return
318
   *   The content of the referenced resource.
319
   */
320
  public function getContent() {
321
    feeds_include_library('http_request.inc', 'http_request');
322
    $result = http_request_get($this->getUrlEncodedValue());
323
    if ($result->code != 200) {
324
      throw new Exception(t('Download of @url failed with code !code.', array('@url' => $this->getUrlEncodedValue(), '!code' => $result->code)));
325
    }
326
    return $result->data;
327
  }
328

    
329
  /**
330
   * Get a Drupal file object of the enclosed resource, download if necessary.
331
   *
332
   * @param $destination
333
   *   The path or uri specifying the target directory in which the file is
334
   *   expected. Don't use trailing slashes unless it's a streamwrapper scheme.
335
   *
336
   * @return
337
   *   A Drupal temporary file object of the enclosed resource.
338
   *
339
   * @throws Exception
340
   *   If file object could not be created.
341
   */
342
  public function getFile($destination) {
343

    
344
    if ($this->getValue()) {
345
      // Prepare destination directory.
346
      file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY);
347
      // Copy or save file depending on whether it is remote or local.
348
      if (drupal_realpath($this->getValue())) {
349
        $file           = new stdClass();
350
        $file->uid      = 0;
351
        $file->uri      = $this->getValue();
352
        $file->filemime = $this->mime_type;
353
        $file->filename = basename($file->uri);
354
        if (dirname($file->uri) != $destination) {
355
          $file = file_copy($file, $destination);
356
        }
357
        else {
358
          // If file is not to be copied, check whether file already exists,
359
          // as file_save() won't do that for us (compare file_copy() and
360
          // file_save())
361
          $existing_files = file_load_multiple(array(), array('uri' => $file->uri));
362
          if (count($existing_files)) {
363
            $existing = reset($existing_files);
364
            $file->fid = $existing->fid;
365
            $file->filename = $existing->filename;
366
          }
367
          file_save($file);
368
        }
369
      }
370
      else {
371
        $filename = basename($this->getLocalValue());
372
        if (module_exists('transliteration')) {
373
          require_once drupal_get_path('module', 'transliteration') . '/transliteration.inc';
374
          $filename = transliteration_clean_filename($filename);
375
        }
376
        if (file_uri_target($destination)) {
377
          $destination = trim($destination, '/') . '/';
378
        }
379
        try {
380
          $file = file_save_data($this->getContent(), $destination . $filename);
381
        }
382
        catch (Exception $e) {
383
          watchdog_exception('Feeds', $e, nl2br(check_plain($e)));
384
        }
385
      }
386

    
387
      // We couldn't make sense of this enclosure, throw an exception.
388
      if (!$file) {
389
        throw new Exception(t('Invalid enclosure %enclosure', array('%enclosure' => $this->getValue())));
390
      }
391
    }
392
    return $file;
393
  }
394
}
395

    
396
/**
397
 * Defines a date element of a parsed result (including ranges, repeat).
398
 */
399
class FeedsDateTimeElement extends FeedsElement {
400

    
401
  // Start date and end date.
402
  public $start;
403
  public $end;
404

    
405
  /**
406
   * Constructor.
407
   *
408
   * @param $start
409
   *   A FeedsDateTime object or a date as accepted by FeedsDateTime.
410
   * @param $end
411
   *   A FeedsDateTime object or a date as accepted by FeedsDateTime.
412
   * @param $tz
413
   *   A PHP DateTimeZone object.
414
   */
415
  public function __construct($start = NULL, $end = NULL, $tz = NULL) {
416
    $this->start = (!isset($start) || ($start instanceof FeedsDateTime)) ? $start : new FeedsDateTime($start, $tz);
417
    $this->end = (!isset($end) || ($end instanceof FeedsDateTime)) ? $end : new FeedsDateTime($end, $tz);
418
  }
419

    
420
  /**
421
   * Override FeedsElement::getValue().
422
   *
423
   * @return
424
   *   The UNIX timestamp of this object's start date. Return value is
425
   *   technically a string but will only contain numeric values.
426
   */
427
  public function getValue() {
428
    if ($this->start) {
429
      return $this->start->format('U');
430
    }
431
    return '0';
432
  }
433

    
434
  /**
435
   * Merge this field with another. Most stuff goes down when merging the two
436
   * sub-dates.
437
   *
438
   * @see FeedsDateTime
439
   */
440
  public function merge(FeedsDateTimeElement $other) {
441
    $this2 = clone $this;
442
    if ($this->start && $other->start) {
443
      $this2->start = $this->start->merge($other->start);
444
    }
445
    elseif ($other->start) {
446
      $this2->start = clone $other->start;
447
    }
448
    elseif ($this->start) {
449
      $this2->start = clone $this->start;
450
    }
451

    
452
    if ($this->end && $other->end) {
453
      $this2->end = $this->end->merge($other->end);
454
    }
455
    elseif ($other->end) {
456
      $this2->end = clone $other->end;
457
    }
458
    elseif ($this->end) {
459
      $this2->end = clone $this->end;
460
    }
461
    return $this2;
462
  }
463

    
464
  /**
465
   * Helper method for buildDateField(). Build a FeedsDateTimeElement object
466
   * from a standard formatted node.
467
   */
468
  protected static function readDateField($entity, $field_name, $delta = 0) {
469
    $ret = new FeedsDateTimeElement();
470
    if (isset($entity->{$field_name}['und'][$delta]['date']) && $entity->{$field_name}['und'][$delta]['date'] instanceof FeedsDateTime) {
471
      $ret->start = $entity->{$field_name}['und'][$delta]['date'];
472
    }
473
    if (isset($entity->{$field_name}['und'][$delta]['date2']) && $entity->{$field_name}['und'][$delta]['date2'] instanceof FeedsDateTime) {
474
      $ret->end = $entity->{$field_name}['und'][$delta]['date2'];
475
    }
476
    return $ret;
477
  }
478

    
479
  /**
480
   * Build a entity's date field from our object.
481
   *
482
   * @param object $entity
483
   *   The entity to build the date field on.
484
   * @param str $field_name
485
   *   The name of the field to build.
486
   * @param int $delta
487
   *   The delta in the field.
488
   */
489
  public function buildDateField($entity, $field_name, $delta = 0) {
490
    $info = field_info_field($field_name);
491

    
492
    $oldfield = FeedsDateTimeElement::readDateField($entity, $field_name, $delta);
493
    // Merge with any preexisting objects on the field; we take precedence.
494
    $oldfield = $this->merge($oldfield);
495
    $use_start = $oldfield->start;
496
    $use_end = $oldfield->end;
497

    
498
    // Set timezone if not already in the FeedsDateTime object
499
    $to_tz = date_get_timezone($info['settings']['tz_handling'], date_default_timezone());
500
    $temp = new FeedsDateTime(NULL, new DateTimeZone($to_tz));
501

    
502
    $db_tz = '';
503
    if ($use_start) {
504
      $use_start = $use_start->merge($temp);
505
      if (!date_timezone_is_valid($use_start->getTimezone()->getName())) {
506
        $use_start->setTimezone(new DateTimeZone("UTC"));
507
      }
508
      $db_tz = date_get_timezone_db($info['settings']['tz_handling'], $use_start->getTimezone()->getName());
509
    }
510
    if ($use_end) {
511
      $use_end = $use_end->merge($temp);
512
      if (!date_timezone_is_valid($use_end->getTimezone()->getName())) {
513
        $use_end->setTimezone(new DateTimeZone("UTC"));
514
      }
515
      if (!$db_tz) {
516
        $db_tz = date_get_timezone_db($info['settings']['tz_handling'], $use_end->getTimezone()->getName());
517
      }
518
    }
519
    if (!$db_tz) {
520
      return;
521
    }
522

    
523
    $db_tz = new DateTimeZone($db_tz);
524
    if (!isset($entity->{$field_name})) {
525
      $entity->{$field_name} = array('und' => array());
526
    }
527
    if ($use_start) {
528
      $entity->{$field_name}['und'][$delta]['timezone'] = $use_start->getTimezone()->getName();
529
      $entity->{$field_name}['und'][$delta]['offset'] = $use_start->getOffset();
530
      $use_start->setTimezone($db_tz);
531
      $entity->{$field_name}['und'][$delta]['date'] = $use_start;
532
      /**
533
       * @todo the date_type_format line could be simplified based upon a patch
534
       *   DO issue #259308 could affect this, follow up on at some point.
535
       *   Without this, all granularity info is lost.
536
       *   $use_start->format(date_type_format($field['type'], $use_start->granularity));
537
       */
538
      $entity->{$field_name}['und'][$delta]['value'] = $use_start->format(date_type_format($info['type']));
539
    }
540
    if ($use_end) {
541
      // Don't ever use end to set timezone (for now)
542
      $entity->{$field_name}['und'][$delta]['offset2'] = $use_end->getOffset();
543
      $use_end->setTimezone($db_tz);
544
      $entity->{$field_name}['und'][$delta]['date2'] = $use_end;
545
      $entity->{$field_name}['und'][$delta]['value2'] = $use_end->format(date_type_format($info['type']));
546
    }
547
  }
548
}
549

    
550
/**
551
 * Extend PHP DateTime class with granularity handling, merge functionality and
552
 * slightly more flexible initialization parameters.
553
 *
554
 * This class is a Drupal independent extension of the >= PHP 5.2 DateTime
555
 * class.
556
 *
557
 * @see FeedsDateTimeElement
558
 */
559
class FeedsDateTime extends DateTime {
560
  public $granularity = array();
561
  protected static $allgranularity = array('year', 'month', 'day', 'hour', 'minute', 'second', 'zone');
562
  private $_serialized_time;
563
  private $_serialized_timezone;
564

    
565
  /**
566
   * Helper function to prepare the object during serialization.
567
   *
568
   * We are extending a core class and core classes cannot be serialized.
569
   *
570
   * Ref: http://bugs.php.net/41334, http://bugs.php.net/39821
571
   */
572
  public function __sleep() {
573
    $this->_serialized_time = $this->format('c');
574
    $this->_serialized_timezone = $this->getTimezone()->getName();
575
    return array('_serialized_time', '_serialized_timezone');
576
  }
577

    
578
  /**
579
   * Upon unserializing, we must re-build ourselves using local variables.
580
   */
581
  public function __wakeup() {
582
    $this->__construct($this->_serialized_time, new DateTimeZone($this->_serialized_timezone));
583
  }
584

    
585
  /**
586
   * Overridden constructor.
587
   *
588
   * @param $time
589
   *   time string, flexible format including timestamp. Invalid formats will
590
   *   fall back to 'now'.
591
   * @param $tz
592
   *   PHP DateTimeZone object, NULL allowed
593
   */
594
  public function __construct($time = '', $tz = NULL) {
595
    // Assume UNIX timestamp if numeric.
596
    if (is_numeric($time)) {
597
      // Make sure it's not a simple year
598
      if ((is_string($time) && strlen($time) > 4) || is_int($time)) {
599
        $time = "@" . $time;
600
      }
601
    }
602

    
603
    // PHP < 5.3 doesn't like the GMT- notation for parsing timezones.
604
    $time = str_replace("GMT-", "-", $time);
605
    $time = str_replace("GMT+", "+", $time);
606

    
607
    // Some PHP 5.2 version's DateTime class chokes on invalid dates.
608
    if (!strtotime($time)) {
609
      $time = 'now';
610
    }
611

    
612
    // Create and set time zone separately, PHP 5.2.6 does not respect time zone
613
    // argument in __construct().
614
    parent::__construct($time);
615
    $tz = $tz ? $tz : new DateTimeZone("UTC");
616
    $this->setTimeZone($tz);
617

    
618
    // Verify that timezone has not been specified as an offset.
619
    if (!preg_match('/[a-zA-Z]/', $this->getTimezone()->getName())) {
620
      $this->setTimezone(new DateTimeZone("UTC"));
621
    }
622

    
623
    // Finally set granularity.
624
    $this->setGranularityFromTime($time, $tz);
625
  }
626

    
627
  /**
628
   * This function will keep this object's values by default.
629
   */
630
  public function merge(FeedsDateTime $other) {
631
    $other_tz = $other->getTimezone();
632
    $this_tz = $this->getTimezone();
633
    // Figure out which timezone to use for combination.
634
    $use_tz = ($this->hasGranularity('zone') || !$other->hasGranularity('zone')) ? $this_tz : $other_tz;
635

    
636
    $this2 = clone $this;
637
    $this2->setTimezone($use_tz);
638
    $other->setTimezone($use_tz);
639
    $val = $this2->toArray();
640
    $otherval = $other->toArray();
641
    foreach (self::$allgranularity as $g) {
642
      if ($other->hasGranularity($g) && !$this2->hasGranularity($g)) {
643
        // The other class has a property we don't; steal it.
644
        $this2->addGranularity($g);
645
        $val[$g] = $otherval[$g];
646
      }
647
    }
648
    $other->setTimezone($other_tz);
649

    
650
    $this2->setDate($val['year'], $val['month'], $val['day']);
651
    $this2->setTime($val['hour'], $val['minute'], $val['second']);
652
    return $this2;
653
  }
654

    
655
  /**
656
   * Overrides default DateTime function. Only changes output values if
657
   * actually had time granularity. This should be used as a "converter" for
658
   * output, to switch tzs.
659
   *
660
   * In order to set a timezone for a datetime that doesn't have such
661
   * granularity, merge() it with one that does.
662
   */
663
  public function setTimezone($tz, $force = FALSE) {
664
    // PHP 5.2.6 has a fatal error when setting a date's timezone to itself.
665
    // http://bugs.php.net/bug.php?id=45038
666
    if (version_compare(PHP_VERSION, '5.2.7', '<') && $tz == $this->getTimezone()) {
667
      $tz = new DateTimeZone($tz->getName());
668
    }
669

    
670
    if (!$this->hasTime() || !$this->hasGranularity('zone') || $force) {
671
      // this has no time or timezone granularity, so timezone doesn't mean much
672
      // We set the timezone using the method, which will change the day/hour, but then we switch back
673
      $arr = $this->toArray();
674
      parent::setTimezone($tz);
675
      $this->setDate($arr['year'], $arr['month'], $arr['day']);
676
      $this->setTime($arr['hour'], $arr['minute'], $arr['second']);
677
      return;
678
    }
679
    parent::setTimezone($tz);
680
  }
681

    
682
  /**
683
   * Safely adds a granularity entry to the array.
684
   */
685
  public function addGranularity($g) {
686
    $this->granularity[] = $g;
687
    $this->granularity = array_unique($this->granularity);
688
  }
689

    
690
  /**
691
   * Removes a granularity entry from the array.
692
   */
693
  public function removeGranularity($g) {
694
    if ($key = array_search($g, $this->granularity)) {
695
      unset($this->granularity[$key]);
696
    }
697
  }
698

    
699
  /**
700
   * Checks granularity array for a given entry.
701
   */
702
  public function hasGranularity($g) {
703
    return in_array($g, $this->granularity);
704
  }
705

    
706
  /**
707
   * Returns whether this object has time set. Used primarily for timezone
708
   * conversion and fomratting.
709
   *
710
   * @todo currently very simplistic, but effective, see usage
711
   */
712
  public function hasTime() {
713
    return $this->hasGranularity('hour');
714
  }
715

    
716
  /**
717
   * Protected function to find the granularity given by the arguments to the
718
   * constructor.
719
   */
720
  protected function setGranularityFromTime($time, $tz) {
721
    $this->granularity = array();
722
    $temp = date_parse($time);
723
    // This PHP method currently doesn't have resolution down to seconds, so if
724
    // there is some time, all will be set.
725
    foreach (self::$allgranularity AS $g) {
726
      if ((isset($temp[$g]) && is_numeric($temp[$g])) || ($g == 'zone' && (isset($temp['zone_type']) && $temp['zone_type'] > 0))) {
727
        $this->granularity[] = $g;
728
      }
729
    }
730
    if ($tz) {
731
      $this->addGranularity('zone');
732
    }
733
  }
734

    
735
  /**
736
   * Helper to return all standard date parts in an array.
737
   */
738
  protected function toArray() {
739
    return array('year' => $this->format('Y'), 'month' => $this->format('m'), 'day' => $this->format('d'), 'hour' => $this->format('H'), 'minute' => $this->format('i'), 'second' => $this->format('s'), 'zone' => $this->format('e'));
740
  }
741
}
742

    
743
/**
744
 * Converts to UNIX time.
745
 *
746
 * @param $date
747
 *   A date that is either a string, a FeedsDateTimeElement or a UNIX timestamp.
748
 * @param $default_value
749
 *   A default UNIX timestamp to return if $date could not be parsed.
750
 *
751
 * @return
752
 *   $date as UNIX time if conversion was successful, $dfeault_value otherwise.
753
 */
754
function feeds_to_unixtime($date, $default_value) {
755
  if (is_numeric($date)) {
756
    return $date;
757
  }
758
  elseif (is_string($date) && !empty($date)) {
759
    $date = new FeedsDateTimeElement($date);
760
    return $date->getValue();
761
  }
762
  elseif ($date instanceof FeedsDateTimeElement) {
763
    return $date->getValue();
764
  }
765
  return $default_value;
766
}