StringBuilder::t()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 7
c 2
b 0
f 0
dl 0
loc 12
rs 10
cc 2
nc 2
nop 2
1
<?php
2
/**
3
 * File containing the {@link StringBuilder} class.
4
 *
5
 * @package Application Utils
6
 * @subpackage StringBuilder
7
 * @see StringBuilder
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
use DateTime;
15
use AppLocalize;
0 ignored issues
show
Bug introduced by
The type AppLocalize was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Exception;
17
use Throwable;
18
19
/**
20
 * Utility class used to easily concatenate strings
21
 * with a chainable interface. 
22
 * 
23
 * Each bit of text that is added is automatically 
24
 * separated by spaces, making it easy to write
25
 * texts without handling this separately.
26
 * 
27
 * Specialized methods help in quickly formatting 
28
 * text, or adding common HTML-based contents.
29
 *
30
 * @package Application Utils
31
 * @subpackage StringBuilder
32
 * @author Sebastian Mordziol <[email protected]>
33
 *
34
 * @see StringBuilder
35
 */
36
class StringBuilder implements StringBuilder_Interface
37
{
38
    public const ERROR_CALLABLE_THREW_ERROR = 99601;
39
40
   /**
41
    * @var string
42
    */
43
    protected $separator = ' ';
44
45
   /**
46
    * @var string[]
47
    */
48
    protected $strings = array();
49
50
   /**
51
    * @var string
52
    */
53
    protected $noSeparator = '§!§';
54
    
55
    public function __construct()
56
    {
57
        
58
    }
59
60
    public function setSeparator(string $separator) : StringBuilder
61
    {
62
        $this->separator = $separator;
63
        return $this;
64
    }
65
    
66
   /**
67
    * Adds a subject as a string. Is ignored if empty.
68
    * 
69
    * @param string|number|Interface_Stringable|NULL $string
70
    * @return $this
71
    */
72
    public function add($string) : StringBuilder
73
    {
74
        $string = (string)$string;
75
        
76
        if(!empty($string)) 
77
        {
78
            $this->strings[] = $string;
79
        }
80
        
81
        return $this;
82
    }
83
    
84
   /**
85
    * Adds a string without appending an automatic space.
86
    * 
87
    * @param string|number|Interface_Stringable|NULL $string
88
    * @return $this
89
    */
90
    public function nospace($string) : StringBuilder
91
    {
92
        $flattened = (string)$string;
93
94
        if($flattened !== "")
95
        {
96
            $this->add($this->noSeparator.$flattened);
97
        }
98
99
        return $this;
100
    }
101
    
102
   /**
103
    * Adds raw HTML code. Does not add an automatic space.
104
    * 
105
    * @param string|number|Interface_Stringable $html
106
    * @return $this
107
    */
108
    public function html($html) : StringBuilder
109
    {
110
        return $this->nospace($html);
111
    }
112
    
113
   /**
114
    * Adds an unordered list with the specified items.
115
    * 
116
    * @param array<int,string|number|Interface_Stringable> $items
117
    * @return $this
118
    */
119
    public function ul(array $items) : StringBuilder
120
    {
121
        return $this->list('ul', $items);
122
    }
123
    
124
   /**
125
    * Adds an ordered list with the specified items.
126
    * 
127
    * @param array<int,string|number|Interface_Stringable> $items
128
    * @return $this
129
    */
130
    public function ol(array $items) : StringBuilder
131
    {
132
        return $this->list('ol', $items);
133
    }
134
    
135
   /**
136
    * Creates a list tag with the items list.
137
    * 
138
    * @param string $type The list type, `ol` or `ul`.
139
    * @param array<int,string|number|Interface_Stringable> $items
140
    * @return $this
141
    */
142
    protected function list(string $type, array $items) : StringBuilder
143
    {
144
        return $this->html(sprintf(
145
            '<%1$s><li>%2$s</li></%1$s>',
146
            $type,
147
            implode('</li><li>', $items)
148
        ));
149
    }
150
    
151
   /**
152
    * Add a translated string.
153
    * 
154
    * @param string $format The native string to translate.
155
    * @param array<int,mixed> $arguments The variables to inject into the translated string, if any.
156
    * @return $this
157
    */
158
    public function t(string $format, ...$arguments) : StringBuilder
159
    {
160
        if(!class_exists('\AppLocalize\Localization'))
161
        {
162
            array_unshift($arguments, $format);
163
            return $this->sf(...$arguments);
164
        }
165
        
166
        return $this->add(call_user_func(
167
            array(AppLocalize\Localization::getTranslator(), 'translate'),
0 ignored issues
show
Bug introduced by
The type AppLocalize\Localization was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
168
            $format,
169
            $arguments
170
        ));
171
    }
172
173
    /**
174
     * Add a translated text with translation context information.
175
     *
176
     * @param string $format The native string to translate.
177
     * @param string $context Translation context hints, shown in the translation UI.
178
     * @param mixed ...$arguments
179
     * @return $this
180
     */
181
    public function tex(string $format, string $context, ...$arguments) : StringBuilder
182
    {
183
        unset($context); // Only used by the localization parser.
184
185
        if(!class_exists('\AppLocalize\Localization'))
186
        {
187
            array_unshift($arguments, $format);
188
            return $this->sf(...$arguments);
189
        }
190
191
        return $this->add(call_user_func(
192
            array(AppLocalize\Localization::getTranslator(), 'translate'),
193
            $format,
194
            $arguments
195
        ));
196
    }
197
198
    /**
199
     * Adds a "5 months ago" age since the specified date.
200
     *
201
     * @param DateTime $since
202
     * @return $this
203
     * @throws ConvertHelper_Exception
204
     */
205
    public function age(DateTime $since) : StringBuilder
206
    {
207
        return $this->add(ConvertHelper::duration2string($since));
208
    }
209
    
210
   /**
211
    * Adds HTML double quotes around the string.
212
    * 
213
    * @param string|number|Interface_Stringable $string
214
    * @return $this
215
    */
216
    public function quote($string) : StringBuilder
217
    {
218
        return $this->sf('&quot;%s&quot;', (string)$string);
219
    }
220
    
221
   /**
222
    * Adds a text that is meant as a reference to a UI element,
223
    * like a menu item, button, etc.
224
    * 
225
    * @param string|number|Interface_Stringable $string 
226
    * @return $this
227
    */
228
    public function reference($string) : StringBuilder
229
    {
230
        return $this->sf('"%s"', $string);
231
    }
232
233
   /**
234
    * Add a string using the `sprintf` method.
235
    * 
236
    * @param string $format The format string
237
    * @param string|number|Interface_Stringable ...$arguments The variables to inject
238
    * @return $this
239
    */
240
    public function sf(string $format, ...$arguments) : StringBuilder
241
    {
242
        array_unshift($arguments, $format);
243
        
244
        return $this->add(sprintf(...$arguments));
245
    }
246
    
247
   /**
248
    * Adds a bold string.
249
    * 
250
    * @param string|number|Interface_Stringable $string
251
    * @return $this
252
    */
253
    public function bold($string) : StringBuilder
254
    {
255
        return $this->sf(
256
            '<b>%s</b>',
257
            (string)$string
258
        );
259
    }
260
    
261
   /**
262
    * Adds an HTML `<br>` tag.
263
    *
264
    * Note: for adding a newline character instead,
265
    * use {@see StringBuilder::eol()}.
266
    * 
267
    * @return $this
268
    * @see StringBuilder::eol()
269
    */
270
    public function nl() : StringBuilder
271
    {
272
        return $this->html('<br>');
273
    }
274
275
    /**
276
     * Adds an EOL character, without space.
277
     *
278
     * @return $this
279
     * @see StringBuilder::nl()
280
     */
281
    public function eol() : StringBuilder
282
    {
283
        return $this->nospace(PHP_EOL);
284
    }
285
    
286
   /**
287
    * Adds the current time, in the format <code>H:i:s</code>.
288
    * 
289
    * @return $this
290
    */
291
    public function time() : StringBuilder
292
    {
293
        return $this->add(date('H:i:s'));
294
    }
295
    
296
   /**
297
    * Adds the "Note:" text.
298
    * 
299
    * @return $this
300
    */
301
    public function note() : StringBuilder
302
    {
303
        return $this->t('Note:');
304
    }
305
    
306
   /**
307
    * Like {@see StringBuilder::note()}, but as bold text.
308
    * 
309
    * @return $this
310
    */
311
    public function noteBold() : StringBuilder
312
    {
313
        return $this->bold(sb()->note());
314
    }
315
    
316
   /**
317
    * Adds the "Hint:" text.
318
    * 
319
    * @return $this
320
    * @see StringBuilder::hintBold()
321
    */
322
    public function hint() : StringBuilder
323
    {
324
        return $this->t('Hint:');
325
    }
326
327
    /**
328
     * Like {@see StringBuilder::hint()}, but as bold text.
329
     *
330
     * @return $this
331
     */
332
    public function hintBold() : StringBuilder
333
    {
334
        return $this->bold(sb()->hint());
335
    }
336
337
   /**
338
    * Adds two linebreaks.
339
    *
340
    * @param StringBuilder_Interface|string|NULL $content
341
    * @return $this
342
    */
343
    public function para($content=null) : StringBuilder
344
    {
345
        if($content !== null) {
346
            return $this->html('<p>')->nospace($content)->html('</p>');
347
        }
348
349
        return $this->nl()->nl();
350
    }
351
352
    /**
353
     * Adds an anchor HTML tag.
354
     *
355
     * @param string $label
356
     * @param string $url
357
     * @param bool $newTab
358
     * @param AttributeCollection|null $attributes
359
     * @return $this
360
     */
361
    public function link(string $label, string $url, bool $newTab=false, ?AttributeCollection $attributes=null) : StringBuilder
362
    {
363
        return $this->add($this->createLink($label, $url, $newTab, $attributes));
364
    }
365
366
    private function createLink(string $label, string $url, bool $newTab=false, ?AttributeCollection $attributes=null) : HTMLTag
367
    {
368
        if($attributes === null)
369
        {
370
            $attributes = AttributeCollection::create();
371
        }
372
373
        $attributes->href($url);
374
375
        if($newTab)
376
        {
377
            $attributes->target();
378
        }
379
380
        return HTMLTag::create('a', $attributes)
381
            ->addText($label);
382
    }
383
384
    public function linkOpen(string $url, bool $newTab=false, ?AttributeCollection $attributes=null) : StringBuilder
385
    {
386
        return $this->html($this->createLink('', $url, $newTab, $attributes)->renderOpen());
387
    }
388
389
    public function linkClose() : StringBuilder
390
    {
391
        return $this->html(HTMLTag::create('a')->renderClose());
392
    }
393
394
   /**
395
    * Wraps the string in a `code` tag.
396
    * 
397
    * @param string|number|Interface_Stringable $string
398
    * @return $this
399
    */
400
    public function code($string) : StringBuilder
401
    {
402
        return $this->sf(
403
            '<code>%s</code>',
404
            (string)$string
405
        );
406
    }
407
    
408
   /**
409
    * Wraps the string in a `pre` tag.
410
    * 
411
    * @param string|number|Interface_Stringable $string
412
    * @return $this
413
    */
414
    public function pre($string) : StringBuilder
415
    {
416
        return $this->sf('<pre>%s</pre>', (string)$string);
417
    }
418
    
419
   /**
420
    * Wraps the text in a `span` tag with the specified classes.
421
    * 
422
    * @param string|number|Interface_Stringable $string
423
    * @param string|string[] $classes
424
    * @return $this
425
    */
426
    public function spanned($string, $classes) : StringBuilder
427
    {
428
        if(!is_array($classes)) 
429
        {
430
            $classes = array((string)$classes);
431
        }
432
        
433
        return $this->sf(
434
            '<span class="%s">%s</span>',
435
            implode(' ', $classes),
436
            (string)$string
437
        );
438
    }
439
440
    /**
441
     * @param string|bool|int $value
442
     * @param bool $yesNo
443
     * @return $this
444
     * @throws ConvertHelper_Exception
445
     */
446
    public function bool($value, bool $yesNo=false) : StringBuilder
447
    {
448
        return $this->add(ConvertHelper::bool2string($value, $yesNo));
449
    }
450
451
    /**
452
     * Adds the specified content only if the condition is true.
453
     * Use a callback to render the content to avoid rendering it
454
     * even if the condition is false.
455
     *
456
     * @param bool $condition
457
     * @param string|number|Interface_Stringable|NULL|callable $content
458
     * @return $this
459
     *
460
     * @throws StringBuilder_Exception
461
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
462
     */
463
    public function ifTrue(bool $condition, $content) : StringBuilder
464
    {
465
        if($condition === true)
466
        {
467
            $this->add($this->renderContent($content));
468
        }
469
470
        return $this;
471
    }
472
473
    /**
474
     * Adds the specified content only if the condition is false.
475
     * Use a callback to render the content to avoid rendering it
476
     * even if the condition is true.
477
     *
478
     * @param bool $condition
479
     * @param string|number|Interface_Stringable|callable|NULL $string
480
     * @return $this
481
     *
482
     * @throws StringBuilder_Exception
483
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
484
     */
485
    public function ifFalse(bool $condition, $string) : StringBuilder
486
    {
487
        if($condition === false)
488
        {
489
            $this->add($this->renderContent($string));
490
        }
491
492
        return $this;
493
    }
494
495
    /**
496
     * Handles callbacks used to render content on demand when
497
     * it is needed. All other values are simply passed through.
498
     *
499
     * @param string|number|Interface_Stringable|callable|NULL $content
500
     * @return string|number|Interface_Stringable|NULL
501
     *
502
     * @throws StringBuilder_Exception
503
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
504
     */
505
    private function renderContent($content)
506
    {
507
        if (!is_callable($content))
508
        {
509
            return $content;
510
        }
511
512
        try
513
        {
514
            return $content();
515
        }
516
        catch (Exception $e)
517
        {
518
            throw new StringBuilder_Exception(
519
                'The callable has thrown an error.',
520
                sprintf(
521
                    'The callable [%s] has thrown an exception when it was called.',
522
                    ConvertHelper::callback2string($content)
523
                ),
524
                self::ERROR_CALLABLE_THREW_ERROR,
525
                $e
526
            );
527
        }
528
    }
529
530
    /**
531
     * @param mixed $subject
532
     * @param string|number|Interface_Stringable|callable|NULL $content
533
     * @return $this
534
     *
535
     * @throws StringBuilder_Exception
536
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
537
     */
538
    public function ifEmpty($subject, $content) : StringBuilder
539
    {
540
        return $this->ifTrue(empty($subject), $content);
541
    }
542
543
    /**
544
     * @param mixed $subject
545
     * @param string|number|Interface_Stringable|callable|NULL $content
546
     * @return $this
547
     *
548
     * @throws StringBuilder_Exception
549
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
550
     */
551
    public function ifNotEmpty($subject, $content) : StringBuilder
552
    {
553
        return $this->ifFalse(empty($subject), $content);
554
    }
555
556
    /**
557
     * Adds the contents depending on the condition is true.
558
     * Use callbacks to render the contents to avoid rendering
559
     * them even when they are not needed.
560
     *
561
     * @param bool $condition
562
     * @param string|number|Interface_Stringable|callable|NULL $ifTrue
563
     * @param string|number|Interface_Stringable|callable|NULL $ifFalse
564
     * @return $this
565
     *
566
     * @throws StringBuilder_Exception
567
     * @see StringBuilder::ERROR_CALLABLE_THREW_ERROR
568
     */
569
    public function ifOr(bool $condition, $ifTrue, $ifFalse) : StringBuilder
570
    {
571
        if($condition === true)
572
        {
573
            return $this->add($this->renderContent($ifTrue));
574
        }
575
576
        return $this->add($this->renderContent($ifFalse));
577
    }
578
579
    public function render() : string
580
    {
581
        $result = implode($this->separator, $this->strings);
582
        
583
        return str_replace(array($this->separator.$this->noSeparator, $this->noSeparator), '', $result);
584
    }
585
    
586
    public function __toString()
587
    {
588
        return $this->render();
589
    }
590
    
591
    public function display() : void
592
    {
593
        echo $this->render();
594
    }
595
}
596