Skip to content

Commit 0aeb6fa

Browse files
committed
Structure, clock and translation
1 parent c53506b commit 0aeb6fa

28 files changed

+516
-291
lines changed

src/DynamicDataAbstract.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ abstract class DynamicDataAbstract
1515

1616
abstract public function get();
1717

18+
/**
19+
* Create a dynamic object that holds a dynamic set of items
20+
*/
1821
public function __construct()
1922
{
2023
$this->data = new \stdClass();

src/Format/Arr.php

100755100644
Lines changed: 27 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,47 @@
1010

1111
namespace MaplePHP\DTO\Format;
1212

13+
use MaplePHP\DTO\Traits\ArrayUtilities;
14+
use MaplePHP\DTO\Traverse;
15+
1316
final class Arr extends FormatAbstract implements FormatInterface
1417
{
18+
use ArrayUtilities;
19+
1520
/**
21+
* This will make the collection accessible through static initiations
1622
* Input is mixed data type in the interface because I do not know the type before
1723
* The class constructor MUST handle the input validation
24+
*
1825
* @param string $value
1926
*/
2027
public function __construct(mixed $value)
2128
{
2229
if (!is_array($value)) {
2330
throw new \InvalidArgumentException("Is expecting a string or a convertable string value.", 1);
2431
}
25-
parent::__construct((array)$value);
32+
parent::__construct($value);
33+
}
34+
35+
/**
36+
* This will access parts of collection that are otherwise not reachable
37+
*
38+
* @param string $name
39+
* @param array $arguments
40+
* @return mixed
41+
*/
42+
function __call(string $name, array $arguments)
43+
{
44+
$inst = new Traverse($this->raw);
45+
if(!method_exists($inst, $name)) {
46+
throw new \BadMethodCallException("Method '$name' does not exist.");
47+
}
48+
return $inst->$name(...$arguments);
2649
}
2750

2851
/**
2952
* Init format by adding data to modify/format/traverse
53+
* '
3054
* @param mixed $value
3155
* @return self
3256
*/
@@ -36,112 +60,13 @@ public static function value(mixed $value): FormatInterface
3660
return $inst;
3761
}
3862

39-
/**
40-
* Unset array
41-
* @return $this
42-
*/
43-
public function unset(): self
44-
{
45-
$args = func_get_args();
46-
foreach ($args as $v) {
47-
unset($this->value[$v]);
48-
}
49-
return $this;
50-
}
51-
5263
/**
5364
* Get array keys
65+
*
5466
* @return self
5567
*/
5668
public function arrayKeys(): self
5769
{
58-
$this->value = array_keys($this->value);
59-
return $this;
60-
}
61-
62-
/**
63-
* Will explode an array item value and then merge it into array in same hierky
64-
* @param string $separator
65-
* @return self
66-
*/
67-
public function arrayItemExpMerge(string $separator): self
68-
{
69-
$new = [];
70-
foreach ($this->value as $item) {
71-
$exp = explode($separator, $item);
72-
$new = array_merge($new, $exp);
73-
}
74-
$this->value = $new;
75-
return $this;
76-
}
77-
78-
public function shift(?Str &$shiftedValue = null): self
79-
{
80-
//$inst = clone $this;
81-
$shiftedValue = array_shift($this->value);
82-
return $this;
83-
}
84-
85-
public function pop(?Str &$poppedValue = null): self
86-
{
87-
//$inst = clone $this;
88-
$poppedValue = array_pop($this->value);
89-
return $this;
90-
}
91-
92-
/**
93-
* Extract all array items with array key prefix ("prefix_"name)
94-
* @param string $search wildcard prefix
95-
* @return self
96-
*/
97-
public function wildcardSearch(string $search): self
98-
{
99-
$regex = "/^" . str_replace(['\*', '\?'], ['.*', '.'], preg_quote($search, '/')) . "$/";
100-
$matches = [];
101-
foreach ($this->value as $element) {
102-
if (preg_match($regex, $element)) {
103-
$matches[] = $element;
104-
}
105-
}
106-
$this->value = $matches;
107-
return $this;
108-
}
109-
110-
111-
/**
112-
* Fill array
113-
* @param int $index
114-
* @param int $times
115-
* @param string $value
116-
* @return self
117-
*/
118-
public function fill(int $index, int $times, string $value = " "): self
119-
{
120-
$this->value = array_fill($index, $times, $value);
121-
return $this;
122-
}
123-
124-
/**
125-
* Return count/length
126-
* @return int
127-
*/
128-
public function count(): int
129-
{
130-
return count($this->value);
131-
}
132-
133-
/**
134-
* Array walk over and make recursive changes to all array items
135-
* @param callable $call return value with changes
136-
* @return self
137-
*/
138-
public function walk(callable $call): self
139-
{
140-
$value = $this->value;
141-
array_walk_recursive($value, function (&$value) use ($call) {
142-
$value = $call($value);
143-
});
144-
$this->value = $value;
145-
return $this;
70+
return $this->keys();
14671
}
14772
}

src/Format/Clock.php

Lines changed: 154 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
namespace MaplePHP\DTO\Format;
1111

1212
use Exception;
13+
use IntlDateFormatter;
1314
use InvalidArgumentException;
1415

1516
final class Clock extends FormatAbstract implements FormatInterface
1617
{
18+
static protected ?string $defaultLocale = 'en';
19+
protected ?string $locale = null;
20+
protected array $parts = [];
21+
1722
/**
1823
* Input is mixed data type in the interface because I do not know the type before
1924
* The class constructor MUST handle the input validation
@@ -30,44 +35,106 @@ public function __construct(mixed $value)
3035
}
3136

3237
/**
33-
* Add translation
38+
* Init format by adding data to modify/format/traverse
39+
* @param mixed $value
40+
* @return self
41+
* @throws Exception
42+
*/
43+
public static function value(mixed $value): FormatInterface
44+
{
45+
return new Clock((string)$value);
46+
}
47+
48+
/**
49+
* Get current expected locale
3450
*
35-
* @param string $langCode
51+
* @return string|null
52+
*/
53+
public function getLocale(): ?string
54+
{
55+
return $this->locale ?? self::$defaultLocale;
56+
}
57+
58+
/**
59+
* Set expected date language using locale
60+
*
61+
* @param string $localeCode
3662
* @return $this
3763
*/
38-
public function setLanguage(string $langCode): self
64+
public function setLanguage(string $localeCode): self
3965
{
66+
$this->locale = $localeCode;
4067
return $this;
4168
}
4269

70+
// Alias to setLanguage
71+
public function setLocale(string $localeCode): self
72+
{
73+
return $this->setLanguage($localeCode);
74+
}
75+
76+
/**
77+
* Set default date language using locale
78+
*
79+
* @param string $localeCode
80+
* @return void
81+
*/
82+
static public function setDefaultLanguage(string $localeCode): void
83+
{
84+
self::$defaultLocale = $localeCode;
85+
}
86+
87+
// Alias to setDefaultLanguage
88+
static public function setDefaultLocale(string $localeCode): void
89+
{
90+
self::setDefaultLocale($localeCode);
91+
}
92+
4393
/**
4494
* Format date data
95+
*
4596
* @param string $format
97+
* @param string|null $locale
4698
* @return object
4799
*/
48-
public function format(string $format = 'Y-m-d H:i:s'): string
100+
public function format(string $format = 'Y-m-d H:i:s', ?string $locale = null): string
49101
{
50-
return $this->value->format($format);
102+
$locale = !is_null($locale) ? $locale : $this->getLocale();
103+
$translations = $this->getTranslationData($this->raw, $locale);
104+
if($translations) {
105+
return str_replace($translations['find'], $translations['replace'], $this->raw->format($format));
106+
}
107+
return $this->raw->format($format);
51108
}
52109

53110
/**
54-
* Get DTO value
55-
* @return mixed
111+
* Get date and time
112+
*
113+
* @return string
56114
*/
57-
public function get(): mixed
115+
public function get(): string
58116
{
59-
return $this->value->format('Y-m-d H:i:s');
117+
return $this->dateTime();
60118
}
61119

62120
/**
63-
* Init format by adding data to modify/format/traverse
64-
* @param mixed $value
65-
* @return self
66-
* @throws Exception
121+
* Get date and time
122+
*
123+
* @return string
67124
*/
68-
public static function value(mixed $value): FormatInterface
125+
public function dateTime(): string
69126
{
70-
return new Clock((string)$value);
127+
return $this->raw->format('Y-m-d H:i:s');
128+
}
129+
130+
/**
131+
* Get date
132+
*
133+
* @return string
134+
*/
135+
public function date(): string
136+
{
137+
return $this->raw->format('Y-m-d');
71138
}
72139

73140
/**
@@ -76,6 +143,77 @@ public static function value(mixed $value): FormatInterface
76143
*/
77144
public function __toString(): string
78145
{
79-
return (string)$this->get();
146+
return $this->get();
147+
}
148+
149+
/**
150+
* Return translation find array
151+
*
152+
* @param \DateTime $date
153+
* @param string $locale
154+
* @return array
155+
*/
156+
protected function getTranslationData(\DateTime $date, string $locale): array
157+
{
158+
159+
if($locale !== "en") {
160+
$translations = $this->getTranslation($locale);
161+
if($translations === false) {
162+
$formatters = $this->getLocaleTranslation($locale);
163+
}
164+
}
165+
166+
if(!isset($translations) && !isset($formatters)) {
167+
return [];
168+
}
169+
170+
return [
171+
'find' => [$date->format('F'), $date->format('M'), $date->format('l'), $date->format('D')],
172+
'replace' => [
173+
$translations['months'][$date->format('F')] ?? $formatters['monthFull']->format($this->raw),
174+
$translations['monthsShort'][$date->format('M')] ?? $formatters['monthShort']->format($this->raw),
175+
$translations['weekdays'][$date->format('l')] ?? $formatters['weekdayFull']->format($this->raw),
176+
$translations['weekdaysShort'][$date->format('D')] ?? $formatters['weekdayShort']->format($this->raw),
177+
]
178+
];
179+
}
180+
181+
/**
182+
* Get static translation if exists
183+
*
184+
* @param string $locale
185+
* @return array|false
186+
*/
187+
protected function getTranslation(string $locale): array|false
188+
{
189+
$translationFile = realpath(__DIR__ . "/../lang/$locale.php");
190+
if($translationFile !== false) {
191+
return require $translationFile;
192+
}
193+
return false;
194+
}
195+
196+
/**
197+
* Retrieves localized month and weekday names.
198+
*
199+
* Falls back to PHP’s IntlDateFormatter if a static translation is unavailable.
200+
* This depends on installed system locales, which may cause inconsistencies
201+
* and be slower than static translations. However, missing locales can be
202+
* installed for greater control.
203+
*
204+
* @param string $locale The locale code (e.g., 'sv_SE', 'en_US').
205+
* @return array IntlDateFormatter instances for full/short month and weekday names.
206+
*/
207+
protected function getLocaleTranslation(string $locale): array
208+
{
209+
if (!isset($this->parts[$locale])) {
210+
$this->parts[$locale] = [
211+
'monthFull' => new IntlDateFormatter($locale, IntlDateFormatter::LONG, IntlDateFormatter::NONE, null, null, 'MMMM'),
212+
'monthShort' => new IntlDateFormatter($locale, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, null, null, 'MMM'),
213+
'weekdayFull' => new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, 'EEEE'),
214+
'weekdayShort'=> new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, 'E')
215+
];
216+
}
217+
return $this->parts[$locale];
80218
}
81219
}

0 commit comments

Comments
 (0)