Patched to Drupal 8.4.8 level. See https://www.drupal.org/sa-core-2018-004 and patch...
[yaffs-website] / web / core / lib / Drupal / Core / Datetime / DrupalDateTime.php
1 <?php
2
3 namespace Drupal\Core\Datetime;
4
5 use Drupal\Component\Datetime\DateTimePlus;
6 use Drupal\Core\StringTranslation\StringTranslationTrait;
7
8 /**
9  * Extends DateTimePlus().
10  *
11  * This class extends the basic component and adds in Drupal-specific
12  * handling, like translation of the format() method.
13  *
14  * Static methods in base class can also be used to create DrupalDateTime objects.
15  * For example:
16  *
17  * DrupalDateTime::createFromArray( array('year' => 2010, 'month' => 9, 'day' => 28) )
18  *
19  * @see \Drupal/Component/Datetime/DateTimePlus.php
20  */
21 class DrupalDateTime extends DateTimePlus {
22
23   use StringTranslationTrait;
24
25   /**
26    * Format string translation cache.
27    */
28   protected $formatTranslationCache;
29
30   /**
31    * Constructs a date object.
32    *
33    * @param string $time
34    *   A date/input_time_adjusted string. Defaults to 'now'.
35    * @param mixed $timezone
36    *   PHP DateTimeZone object, string or NULL allowed.
37    *   Defaults to NULL. Note that the $timezone parameter and the current
38    *   timezone are ignored when the $time parameter either is a UNIX timestamp
39    *   (e.g. @946684800) or specifies a timezone
40    *   (e.g. 2010-01-28T15:00:00+02:00).
41    *   @see http://php.net/manual/en/datetime.construct.php
42    * @param array $settings
43    *   - validate_format: (optional) Boolean choice to validate the
44    *     created date using the input format. The format used in
45    *     createFromFormat() allows slightly different values than format().
46    *     Using an input format that works in both functions makes it
47    *     possible to a validation step to confirm that the date created
48    *     from a format string exactly matches the input. This option
49    *     indicates the format can be used for validation. Defaults to TRUE.
50    *   - langcode: (optional) Used to control the result of the format() method.
51    *     Defaults to NULL.
52    *   - debug: (optional) Boolean choice to leave debug values in the
53    *     date object for debugging purposes. Defaults to FALSE.
54    */
55   public function __construct($time = 'now', $timezone = NULL, $settings = []) {
56     if (!isset($settings['langcode'])) {
57       $settings['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
58     }
59
60     // Instantiate the parent class.
61     parent::__construct($time, $timezone, $settings);
62
63   }
64
65   /**
66    * Overrides prepareTimezone().
67    *
68    * Override basic component timezone handling to use Drupal's
69    * knowledge of the preferred user timezone.
70    */
71   protected function prepareTimezone($timezone) {
72     if (empty($timezone)) {
73       // Fallback to user or system default timezone.
74       $timezone = drupal_get_user_timezone();
75     }
76     return parent::prepareTimezone($timezone);
77   }
78
79   /**
80    * Overrides format().
81    *
82    * @param string $format
83    *   A format string using either PHP's date().
84    * @param array $settings
85    *   - timezone: (optional) String timezone name. Defaults to the timezone
86    *     of the date object.
87    *   - langcode: (optional) String two letter language code used to control
88    *     the result of the format() method. Defaults to NULL.
89    *
90    * @return string
91    *   The formatted value of the date. Since the format may contain user input,
92    *   this value should be escaped when output.
93    */
94   public function format($format, $settings = []) {
95     $langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
96     $value = '';
97     // Format the date and catch errors.
98     try {
99       // Encode markers that should be translated. 'A' becomes
100       // '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
101       // and we assume they are not in the input string.
102       // Paired backslashes are isolated to prevent errors in
103       // read-ahead evaluation. The read-ahead expression ensures that
104       // A matches, but not \A.
105       $format = preg_replace(['/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'], ["\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"], $format);
106
107       // Call date_format().
108       $format = parent::format($format, $settings);
109
110       // Translates a formatted date string.
111       $translation_callback = function ($matches) use ($langcode) {
112         $code = $matches[1];
113         $string = $matches[2];
114         if (!isset($this->formatTranslationCache[$langcode][$code][$string])) {
115           $options = ['langcode' => $langcode];
116           if ($code == 'F') {
117             $options['context'] = 'Long month name';
118           }
119
120           if ($code == '') {
121             $this->formatTranslationCache[$langcode][$code][$string] = $string;
122           }
123           else {
124             $this->formatTranslationCache[$langcode][$code][$string] = $this->t($string, [], $options);
125           }
126         }
127         return $this->formatTranslationCache[$langcode][$code][$string];
128       };
129
130       // Translate the marked sequences.
131       $value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', $translation_callback, $format);
132     }
133     catch (\Exception $e) {
134       $this->errors[] = $e->getMessage();
135     }
136     return $value;
137   }
138
139 }