Completed
Push — master ( fb798a...9bcf74 )
by
unknown
17s queued 10s
created

MailEclipse::generateClassName()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 26
rs 9.6111
1
<?php
2
3
namespace Qoraiche\MailEclipse;
4
5
use ErrorException;
6
use Illuminate\Container\Container;
7
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
8
use Illuminate\Mail\Markdown;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Facades\Artisan;
11
use Illuminate\Support\Facades\DB;
12
use Illuminate\Support\Facades\View;
13
use Illuminate\Support\Str;
14
use RecursiveDirectoryIterator;
15
use RecursiveIteratorIterator;
16
use ReeceM\Mocker\Mocked;
17
use ReflectionClass;
18
use ReflectionProperty;
19
use RegexIterator;
20
21
class MailEclipse
22
{
23
    public static $view_namespace = 'maileclipse';
24
25
    /**
26
     * Default type examples for being passed to reflected classes.
27
     *
28
     * @var array TYPES
29
     */
30
    public const TYPES = [
31
        'int' => 31,
32
        // 'string' => 'test_string', // not needed as it can be cast __toString()
33
        'bool' => false,
34
        'float' => 3.14159,
35
    ];
36
37
    public static function getMailables()
38
    {
39
        return self::mailablesList();
40
    }
41
42
    public static function getMailable($key, $name)
43
    {
44
        $filtered = collect(self::getMailables())->where($key, $name);
45
46
        return $filtered;
47
    }
48
49
    public static function deleteTemplate($templateSlug)
50
    {
51
        $template = self::getTemplates()
52
            ->where('template_slug', $templateSlug)->first();
53
54
        if (! is_null($template)) {
55
            self::saveTemplates(self::getTemplates()->reject(function ($value, $key) use ($template) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

55
            self::saveTemplates(self::getTemplates()->reject(function ($value, /** @scrutinizer ignore-unused */ $key) use ($template) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
56
                return $value->template_slug == $template->template_slug;
57
            }));
58
59
            $template_view = self::$view_namespace.'::templates.'.$templateSlug;
60
            $template_plaintext_view = $template_view.'_plain_text';
61
62
            if (View::exists($template_view)) {
63
                unlink(View($template_view)->getPath());
64
65
                if (View::exists($template_plaintext_view)) {
66
                    unlink(View($template_plaintext_view)->getPath());
67
                }
68
69
                return true;
70
            }
71
        }
72
73
        return false;
74
    }
75
76
    public static function getTemplatesFile()
77
    {
78
        $file = config('maileclipse.mailables_dir').'templates.json';
79
        if (! file_exists($file)) {
80
            if (! file_exists(config('maileclipse.mailables_dir'))) {
81
                mkdir(config('maileclipse.mailables_dir'));
82
            }
83
            file_put_contents($file, '[]');
84
        }
85
86
        return $file;
87
    }
88
89
    public static function saveTemplates(Collection $templates)
90
    {
91
        file_put_contents(self::getTemplatesFile(), $templates->toJson());
92
    }
93
94
    public static function updateTemplate($request)
95
    {
96
        $template = self::getTemplates()
97
            ->where('template_slug', $request->templateslug)->first();
98
99
        if (! is_null($template)) {
100
            if (! preg_match("/^[a-zA-Z0-9-_\s]+$/", $request->title)) {
101
                return response()->json([
102
                    'status' => 'failed',
103
                    'message' => 'Template name not valid',
104
                ]);
105
            }
106
107
            $templatename = Str::camel(preg_replace('/\s+/', '_', $request->title));
108
109
            // check if not already exists on db
110
            //
111
            //
112
113
            if (self::getTemplates()->contains('template_slug', '=', $templatename)) {
114
                return response()->json([
115
116
                    'status' => 'failed',
117
                    'message' => 'Template name already exists',
118
119
                ]);
120
            }
121
122
            // Update
123
            //
124
            $oldForm = self::getTemplates()->reject(function ($value, $key) use ($template) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

124
            $oldForm = self::getTemplates()->reject(function ($value, /** @scrutinizer ignore-unused */ $key) use ($template) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
125
                return $value->template_slug == $template->template_slug;
126
            });
127
            $newForm = array_merge($oldForm->toArray(), [array_merge((array) $template, [
128
                'template_slug' => $templatename,
129
                'template_name' => $request->title,
130
                'template_description' => $request->description,
131
            ])]);
132
133
            self::saveTemplates(collect($newForm));
134
135
            $template_view = self::$view_namespace.'::templates.'.$request->templateslug;
136
            $template_plaintext_view = $template_view.'_plain_text';
137
138
            if (View::exists($template_view)) {
139
                $viewPath = View($template_view)->getPath();
140
141
                rename($viewPath, dirname($viewPath)."/{$templatename}.blade.php");
142
143
                if (View::exists($template_plaintext_view)) {
144
                    $textViewPath = View($template_plaintext_view)->getPath();
145
146
                    rename($textViewPath, dirname($viewPath)."/{$templatename}_plain_text.blade.php");
147
                }
148
            }
149
150
            return response()->json([
151
152
                'status' => 'ok',
153
                'message' => 'Updated Successfully',
154
                'template_url' => route('viewTemplate', ['templatename' => $templatename]),
155
156
            ]);
157
        }
158
    }
159
160
    public static function getTemplate($templateSlug)
161
    {
162
        $template = self::getTemplates()
163
            ->where('template_slug', $templateSlug)->first();
164
165
        if (! is_null($template)) {
166
            $template_view = self::$view_namespace.'::templates.'.$template->template_slug;
167
            $template_plaintext_view = $template_view.'_plain_text';
168
169
            // return $template_plaintext_view;
170
171
            if (View::exists($template_view)) {
172
                $viewPath = View($template_view)->getPath();
173
                $textViewPath = View($template_plaintext_view)->getPath();
174
175
                $templateData = collect([
176
                    'template' => self::templateComponentReplace(file_get_contents($viewPath), true),
177
                    'plain_text' => View::exists($template_plaintext_view) ? file_get_contents($textViewPath) : '',
178
                    'slug' => $template->template_slug,
179
                    'name' => $template->template_name,
180
                    'description' => $template->template_description,
181
                    'template_type' => $template->template_type,
182
                    'template_view_name' => $template->template_view_name,
183
                    'template_skeleton' => $template->template_skeleton,
184
                ]);
185
186
                return $templateData;
187
            }
188
        }
189
190
        // return;
191
    }
192
193
    public static function getTemplates()
194
    {
195
        $template = collect(json_decode(file_get_contents(self::getTemplatesFile())));
196
197
        return $template;
198
    }
199
200
    public static function createTemplate($request)
201
    {
202
        if (! preg_match("/^[a-zA-Z0-9-_\s]+$/", $request->template_name)) {
203
            return response()->json([
204
205
                'status' => 'error',
206
                'message' => 'Template name not valid',
207
208
            ]);
209
        }
210
211
        $view = self::$view_namespace.'::templates.'.$request->template_name;
212
213
        $templatename = Str::camel(preg_replace('/\s+/', '_', $request->template_name));
214
215
        if (! view()->exists($view) && ! self::getTemplates()->contains('template_slug', '=', $templatename)) {
216
            self::saveTemplates(self::getTemplates()
217
                ->push([
218
                    'template_name' => $request->template_name,
219
                    'template_slug' => $templatename,
220
                    'template_description' => $request->template_description,
221
                    'template_type' => $request->template_type,
222
                    'template_view_name' => $request->template_view_name,
223
                    'template_skeleton' => $request->template_skeleton,
224
                ]));
225
226
            $dir = resource_path('views/vendor/'.self::$view_namespace.'/templates');
227
228
            if (! \File::isDirectory($dir)) {
229
                \File::makeDirectory($dir, 0755, true);
230
            }
231
232
            file_put_contents($dir."/{$templatename}.blade.php", self::templateComponentReplace($request->content));
233
234
            file_put_contents($dir."/{$templatename}_plain_text.blade.php", $request->plain_text);
235
236
            return response()->json([
237
238
                'status' => 'ok',
239
                'message' => 'Template created<br> <small>Reloading...<small>',
240
                'template_url' => route('viewTemplate', ['templatename' => $templatename]),
241
242
            ]);
243
        }
244
245
        return response()->json([
246
247
            'status' => 'error',
248
            'message' => 'Template not created',
249
250
        ]);
251
    }
252
253
    public static function getTemplateSkeletons()
254
    {
255
        return collect(config('maileclipse.skeletons'));
256
    }
257
258
    public static function getTemplateSkeleton($type, $name, $skeleton)
259
    {
260
        $skeletonView = self::$view_namespace."::skeletons.{$type}.{$name}.{$skeleton}";
261
262
        if (view()->exists($skeletonView)) {
263
            $skeletonViewPath = View($skeletonView)->getPath();
264
            $templateContent = file_get_contents($skeletonViewPath);
265
266
            return [
267
                'type' => $type,
268
                'name' => $name,
269
                'skeleton' => $skeleton,
270
                'template' => self::templateComponentReplace($templateContent, true),
271
                'view' => $skeletonView,
272
                'view_path' => $skeletonViewPath,
273
            ];
274
        }
275
    }
276
277
    protected static function templateComponentReplace($content, $reverse = false)
278
    {
279
        if ($reverse) {
280
            $patterns = [
281
                '/@component/i',
282
                '/@endcomponent/i',
283
                '/@yield/',
284
                '/@section/',
285
                '/@endsection/',
286
                '/@extends/',
287
                '/@parent/',
288
                '/@slot/',
289
                '/@endslot/',
290
            ];
291
292
            $replacements = [
293
                '[component]: # ',
294
                '[endcomponent]: # ',
295
                '[yield]: # ',
296
                '[section]: # ',
297
                '[endsection]: # ',
298
                '[extends]: # ',
299
                '[parent]: # ',
300
                '[slot]: # ',
301
                '[endslot]: # ',
302
            ];
303
        } else {
304
            $patterns = [
305
                '/\[component]:\s?#\s?/i',
306
                '/\[endcomponent]:\s?#\s?/i',
307
                '/\[yield]:\s?#\s?/i',
308
                '/\[section]:\s?#\s?/i',
309
                '/\[endsection]:\s?#\s?/i',
310
                '/\[extends]:\s?#\s?/i',
311
                '/\[parent]:\s?#\s?/i',
312
                '/\[slot]:\s?#\s?/i',
313
                '/\[endslot]:\s?#\s?/i',
314
            ];
315
316
            $replacements = [
317
                '@component',
318
                '@endcomponent',
319
                '@yield',
320
                '@section',
321
                '@endsection',
322
                '@extends',
323
                '@parent',
324
                '@slot',
325
                '@endslot',
326
            ];
327
        }
328
329
        return preg_replace($patterns, $replacements, $content);
330
    }
331
332
    protected static function markdownedTemplate($viewPath)
333
    {
334
        $viewContent = file_get_contents($viewPath);
335
336
        return self::templateComponentReplace($viewContent, true);
337
338
        // return preg_replace($patterns, $replacements, $viewContent);
339
    }
340
341
    /**
342
     * Markdowned template view.
343
     */
344
    public static function markdownedTemplateToView($save = true, $content = '', $viewPath = '', $template = false)
345
    {
346
        if ($template && View::exists(self::$view_namespace.'::templates.'.$viewPath)) {
347
            $viewPath = View(self::$view_namespace.'::templates.'.$viewPath)->getPath();
348
        }
349
350
        $replaced = self::templateComponentReplace($content);
351
352
        if (! $save) {
353
            return $replaced;
354
        }
355
356
        return file_put_contents($viewPath, $replaced) === false ? false : true;
357
    }
358
359
    public static function previewMarkdownViewContent($simpleview, $content, $viewName, $template = false, $namespace = null)
360
    {
361
        $previewtoset = self::markdownedTemplateToView(false, $content);
362
        $dir = dirname(__FILE__, 2).'/resources/views/draft';
363
        $viewName = $template ? $viewName.'_template' : $viewName;
364
365
        if (file_exists($dir)) {
366
            file_put_contents($dir."/{$viewName}.blade.php", $previewtoset);
367
368
            if ($template) {
369
                $instance = null;
370
            } else {
371
                if (! is_null(self::handleMailableViewDataArgs($namespace))) {
372
                    $instance = self::handleMailableViewDataArgs($namespace);
373
                } else {
374
                    $instance = new $namespace;
375
                }
376
            }
377
378
            return self::renderPreview($simpleview, self::$view_namespace.'::draft.'.$viewName, $template, $instance);
379
        }
380
381
        return false;
382
    }
383
384
    public static function previewMarkdownHtml($instance, $view)
385
    {
386
        return self::renderPreview($instance, $view);
387
    }
388
389
    public static function getMailableTemplateData($mailableName)
390
    {
391
        $mailable = self::getMailable('name', $mailableName);
392
393
        if ($mailable->isEmpty()) {
394
            return false;
395
        }
396
397
        $templateData = collect($mailable->first())->only(['markdown', 'view_path', 'text_view_path', 'text_view', 'view_data', 'data', 'namespace'])->all();
398
399
        $templateExists = ! is_null($templateData['view_path']);
400
        $textTemplateExists = ! is_null($templateData['text_view_path']);
401
402
        if ($templateExists) {
403
            $viewPathParams = collect($templateData)->union([
404
405
                'text_template' => $textTemplateExists ? file_get_contents($templateData['text_view_path']) : null,
406
                'template' => file_get_contents($templateData['view_path']),
407
                'markdowned_template' => self::markdownedTemplate($templateData['view_path']),
408
                'template_name' => ! is_null($templateData['markdown']) ? $templateData['markdown'] : $templateData['data']->view,
409
                'is_markdown' => ! is_null($templateData['markdown']) ? true : false,
410
                // 'text_template' => file_get_contents($templateData['text_view_path']),
411
412
            ])->all();
413
414
            return $viewPathParams;
415
        }
416
417
        return $templateData;
418
    }
419
420
    public static function generateMailable($request = null)
421
    {
422
        $name = self::generateClassName($request->input('name'));
423
424
        if ($name === false) {
425
            return response()->json([
426
                'status' => 'error',
427
                'message' => 'Wrong name format.',
428
            ]);
429
        }
430
431
        if (! self::getMailable('name', $name)->isEmpty() && ! $request->has('force')) {
432
            // return redirect()->route('createMailable')->with('error', 'mailable already exists! to overide it enable force option.');
433
            //
434
            return response()->json([
435
                'status' => 'error',
436
                'message' => 'This mailable name already exists. names should be unique! to override it, enable "force" option.',
437
            ]);
438
        }
439
440
        if (strtolower($name) === 'mailable') {
441
            return response()->json([
442
                'status' => 'error',
443
                'message' => 'You cannot use this name',
444
            ]);
445
        }
446
447
        $params = collect([
448
            'name' => $name,
449
        ]);
450
451
        if ($request->input('markdown')) {
452
            $params->put('--markdown', $request->markdown);
453
        }
454
455
        if ($request->has('force')) {
456
            $params->put('--force', true);
457
        }
458
459
        $exitCode = Artisan::call('make:mail', $params->all());
460
461
        if ($exitCode > -1) {
462
463
            // return redirect()->route('mailableList');
464
            //
465
            return response()->json([
466
467
                'status' => 'ok',
468
                'message' => 'Mailable Created<br> <small>Reloading...<small>',
469
470
            ]);
471
        }
472
    }
473
474
    /**
475
     * Get mailables list.
476
     *
477
     * @return array
478
     */
479
    protected static function mailablesList()
480
    {
481
        $fqcns = [];
482
483
        if (! file_exists(config('maileclipse.mailables_dir'))) {
484
            return;
485
        } else {
486
            $allFiles = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(config('maileclipse.mailables_dir')));
487
            $phpFiles = new RegexIterator($allFiles, '/\.php$/');
488
            $i = 0;
489
490
            foreach ($phpFiles as $phpFile) {
491
                $i++;
492
                $content = file_get_contents($phpFile->getRealPath());
493
                $tokens = token_get_all($content);
494
                $namespace = '';
495
                for ($index = 0; isset($tokens[$index]); $index++) {
496
                    if (! isset($tokens[$index][0])) {
497
                        continue;
498
                    }
499
                    if (T_NAMESPACE === $tokens[$index][0]) {
500
                        $index += 2; // Skip namespace keyword and whitespace
501
                        while (isset($tokens[$index]) && is_array($tokens[$index])) {
502
                            $namespace .= $tokens[$index++][1];
503
                        }
504
                    }
505
                    if (T_CLASS === $tokens[$index][0] && T_WHITESPACE === $tokens[$index + 1][0] && T_STRING === $tokens[$index + 2][0]) {
506
                        $index += 2; // Skip class keyword and whitespace
507
508
                        [$name, $extension] = explode('.', $phpFile->getFilename());
509
510
                        $mailableClass = $namespace.'\\'.$tokens[$index][1];
511
512
                        if (! self::mailable_exists($mailableClass)) {
513
                            continue;
514
                        }
515
516
                        $reflector = new ReflectionClass($mailableClass);
517
518
                        if ($reflector->isAbstract()) {
519
                            continue;
520
                        }
521
522
                        $mailable_data = self::buildMailable($mailableClass);
523
524
                        if (! is_null(self::handleMailableViewDataArgs($mailableClass))) {
525
                            $mailable_view_data = self::getMailableViewData(self::handleMailableViewDataArgs($mailableClass), $mailable_data);
526
                        } else {
527
                            $mailable_view_data = self::getMailableViewData(new $mailableClass, $mailable_data);
528
                        }
529
530
                        $fqcns[$i]['data'] = $mailable_data;
531
                        $fqcns[$i]['markdown'] = self::getMarkdownViewName($mailable_data);
532
                        $fqcns[$i]['name'] = $name;
533
                        $fqcns[$i]['namespace'] = $mailableClass;
534
                        $fqcns[$i]['filename'] = $phpFile->getFilename();
535
                        $fqcns[$i]['modified'] = $phpFile->getCTime();
536
                        $fqcns[$i]['viewed'] = $phpFile->getATime();
537
                        $fqcns[$i]['view_data'] = $mailable_view_data;
538
                        // $fqcns[$i]['view_data'] = [];
539
                        $fqcns[$i]['path_name'] = $phpFile->getPathname();
540
                        $fqcns[$i]['readable'] = $phpFile->isReadable();
541
                        $fqcns[$i]['writable'] = $phpFile->isWritable();
542
                        $fqcns[$i]['view_path'] = null;
543
                        $fqcns[$i]['text_view_path'] = null;
544
545
                        if (! is_null($fqcns[$i]['markdown']) && View::exists($fqcns[$i]['markdown'])) {
546
                            $fqcns[$i]['view_path'] = View($fqcns[$i]['markdown'])->getPath();
547
                        }
548
549
                        if (! is_null($fqcns[$i]['data'])) {
550
                            if (! is_null($fqcns[$i]['data']->view) && View::exists($fqcns[$i]['data']->view)) {
551
                                $fqcns[$i]['view_path'] = View($fqcns[$i]['data']->view)->getPath();
552
                            }
553
554
                            if (! is_null($fqcns[$i]['data']->textView) && View::exists($fqcns[$i]['data']->textView)) {
555
                                $fqcns[$i]['text_view_path'] = View($fqcns[$i]['data']->textView)->getPath();
556
                                $fqcns[$i]['text_view'] = $fqcns[$i]['data']->textView;
557
                            }
558
                        }
559
560
                        // break if you have one class per file (psr-4 compliant)
561
                        // otherwise you'll need to handle class constants (Foo::class)
562
                        break;
563
                    }
564
                }
565
            }
566
567
            $collection = collect($fqcns)->map(function ($mailable) {
568
                return $mailable;
569
            })->reject(function ($object) {
570
                return ! method_exists($object['namespace'], 'build');
571
            });
572
573
            return $collection;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $collection returns the type Illuminate\Support\Collection which is incompatible with the documented return type array.
Loading history...
574
        }
575
    }
576
577
    /**
578
     * Handle Mailable Constructor arguments and pass the fake ones.
579
     */
580
    public static function handleMailableViewDataArgs($mailable)
581
    {
582
        if (method_exists($mailable, '__construct')) {
583
            $reflection = new ReflectionClass($mailable);
584
585
            $params = $reflection->getConstructor()->getParameters();
586
587
            DB::beginTransaction();
588
589
            $eloquentFactory = app(EloquentFactory::class);
590
591
            $args = collect($params)->map(function ($param) {
592
                if ($param->getType() !== null) {
593
                    if (class_exists($param->getType()->getName())) {
594
                        $parameters = [
595
                            'is_instance' => true,
596
                            'instance' => $param->getType()->getName(),
597
                        ];
598
                    } elseif ($param->getType()->getName() == 'array') {
599
                        $parameters = [
600
                            'is_array' => true,
601
                            'arg' => $param->getName(),
602
                        ];
603
                    } else {
604
                        $parameters = $param->name;
605
                    }
606
607
                    return $parameters;
608
                }
609
610
                return $param->name;
611
            });
612
613
            $filteredparams = [];
614
615
            foreach ($args->all() as $arg) {
616
                $factoryStates = [];
617
618
                if (is_array($arg)) {
619
                    if (isset($arg['is_instance'])) {
620
                        if (isset($eloquentFactory[$arg['instance']]) && config('maileclipse.factory')) {
621
                            $filteredparams[] = factory($arg['instance'])->states($factoryStates)->make();
622
                        } else {
623
                            $filteredparams[] = app($arg['instance']);
624
                        }
625
                    } elseif (isset($arg['is_array'])) {
626
                        $filteredparams[] = [];
627
                    } else {
628
                        return;
629
                    }
630
                } else {
631
                    $filteredparams[] = self::getMissingParams($arg, $params);
632
                }
633
            }
634
635
            $reflector = new ReflectionClass($mailable);
636
637
            if (! $args->isEmpty()) {
638
                $foo = $reflector->newInstanceArgs($filteredparams);
639
640
                return $foo;
641
            }
642
643
            DB::rollBack();
644
        }
645
    }
646
647
    /**
648
     * Gets any missing params that may not be collectable in the reflection.
649
     *
650
     * @param string $arg the argument string|array
651
     * @param array $params the reflection param list
652
     *
653
     * @return array|string|\ReeceM\Mocker\Mocked
654
     */
655
    private static function getMissingParams($arg, $params)
656
    {
657
        /**
658
         * Determine if a builtin type can be found.
659
         * Not a string or object as a Mocked::class can work there.
660
         *
661
         * getName() is undocumented alternative to casting to string.
662
         * https://www.php.net/manual/en/class.reflectiontype.php#124658
663
         *
664
         * @var \ReflectionType $reflection
665
         */
666
        $reflection = collect($params)->where('name', $arg)->first()->getType();
667
668
        if (version_compare(phpversion(), '7.1', '>=')) {
669
            $type = ! is_null($reflection)
670
                ? self::TYPES[$reflection->getName()]
671
                : null;
672
        } else {
673
            $type = ! is_null($reflection)
674
                ? self::TYPES[/** @scrutinizer ignore-deprecated */ $reflection->__toString()]
675
                : null;
676
        }
677
678
        try {
679
            return ! is_null($type)
680
                    ? $type
681
                    : new Mocked($arg, \ReeceM\Mocker\Utils\VarStore::singleton());
682
        } catch (\Exception $e) {
683
            return $arg;
684
        }
685
    }
686
687
    private static function getMailableViewData($mailable, $mailable_data)
688
    {
689
        $traitProperties = [];
690
691
        $data = new ReflectionClass($mailable);
692
693
        foreach ($data->getTraits() as $trait) {
694
            foreach ($trait->getProperties(ReflectionProperty::IS_PUBLIC) as $traitProperty) {
695
                $traitProperties[] = $traitProperty->name;
696
            }
697
        }
698
699
        $properties = $data->getProperties(ReflectionProperty::IS_PUBLIC);
700
        $allProps = [];
701
702
        foreach ($properties as $prop) {
703
            if ($prop->class == $data->getName() || $prop->class == get_parent_class($data->getName()) &&
704
                    get_parent_class($data->getName()) != 'Illuminate\Mail\Mailable' && ! $prop->isStatic()) {
705
                $allProps[] = $prop->name;
706
            }
707
        }
708
709
        $obj = self::buildMailable($mailable);
710
711
        if (is_null($obj)) {
712
            $obj = [];
713
714
            return collect($obj);
715
        }
716
717
        $classProps = array_diff($allProps, $traitProperties);
718
719
        $withFuncData = collect($obj->viewData)->keys();
720
721
        $mailableData = collect($classProps)->merge($withFuncData);
722
723
        $data = $mailableData->map(function ($parameter) use ($mailable_data) {
724
            return [
725
                'key' => $parameter,
726
                'value' => property_exists($mailable_data, $parameter) ? $mailable_data->$parameter : null,
727
                'data' => property_exists($mailable_data, $parameter) ? self::viewDataInspect($mailable_data->$parameter) : null,
728
            ];
729
        });
730
731
        return $data->all();
732
    }
733
734
    protected static function viewDataInspect($param)
735
    {
736
        if ($param instanceof \Illuminate\Database\Eloquent\Model) {
737
            return [
738
                'type' => 'model',
739
                'attributes' => collect($param->getAttributes()),
740
            ];
741
        } elseif ($param instanceof \Illuminate\Database\Eloquent\Collection) {
742
            return [
743
                'type' => 'elequent-collection',
744
                'attributes' => $param->all(),
745
            ];
746
        } elseif ($param instanceof \Illuminate\Support\Collection) {
747
            return [
748
                'type' => 'collection',
749
                'attributes' => $param->all(),
750
            ];
751
        }
752
    }
753
754
    protected static function mailable_exists($mailable)
755
    {
756
        if (! class_exists($mailable)) {
757
            return false;
758
        }
759
760
        return true;
761
    }
762
763
    protected static function getMarkdownViewName($mailable)
764
    {
765
        if ($mailable === null) {
766
            return;
767
        }
768
769
        $reflection = new ReflectionClass($mailable);
770
771
        $property = $reflection->getProperty('markdown');
772
773
        $property->setAccessible(true);
774
775
        return $property->getValue($mailable);
776
    }
777
778
    public static function buildMailable($instance, $type = 'call')
779
    {
780
        if ($type == 'call') {
781
            if (! is_null(self::handleMailableViewDataArgs($instance))) {
782
                return Container::getInstance()->call([self::handleMailableViewDataArgs($instance), 'build']);
783
            }
784
785
            return Container::getInstance()->call([new $instance, 'build']);
786
        }
787
788
        return Container::getInstance()->make($instance);
789
    }
790
791
    public static function renderPreview($simpleview, $view, $template = false, $instance = null)
792
    {
793
        if (! View::exists($view)) {
794
            return;
795
        }
796
797
        if (! $template) {
798
            $obj = self::buildMailable($instance);
799
            $viewData = $obj->viewData;
800
            $_data = array_merge($instance->buildViewData(), $viewData);
801
802
            foreach ($_data as $key => $value) {
803
                if (! is_object($value)) {
804
                    $_data[$key] = '<span class="maileclipse-key" title="Variable">'.$key.'</span>';
805
                }
806
            }
807
        } else {
808
            $_data = [];
809
        }
810
811
        $_view = $view;
812
813
        try {
814
            if ($simpleview) {
815
                return htmlspecialchars_decode(view($_view, $_data)->render());
0 ignored issues
show
Bug introduced by
It seems like view($_view, $_data)->render() can also be of type array; however, parameter $string of htmlspecialchars_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

815
                return htmlspecialchars_decode(/** @scrutinizer ignore-type */ view($_view, $_data)->render());
Loading history...
816
            }
817
818
            $_md = self::buildMailable(Markdown::class, 'make');
819
820
            return htmlspecialchars_decode($_md->render($_view, $_data));
821
        } catch (ErrorException $e) {
822
            $error = '<div class="alert alert-warning">
823
	    	<h5 class="alert-heading">Error:</h5>
824
	    	<p>'.$e->getMessage().'</p>
825
	    	</div>';
826
827
            if ($template) {
828
                $error .= '<div class="alert alert-info">
829
				<h5 class="alert-heading">Notice:</h5>
830
				<p>You can\'t add variables within a template editor because they are undefined until you bind the template with a mailable that actually has data.</p>
831
	    	</div>';
832
            }
833
834
            return $error;
835
        }
836
    }
837
838
    /**
839
     * Class name has to satisfy those rules.
840
     *
841
     * https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class
842
     * https://www.php.net/manual/en/reserved.keywords.php
843
     *
844
     * @param $input
845
     * @return string|false class name or false on failure
846
     */
847
    public static function generateClassName($input)
848
    {
849
        $suffix = 'Mail';
850
851
        if (strtolower($input) === strtolower($suffix)) {
852
            return false;
853
        }
854
855
        // Avoid MailMail as a class name suffix
856
        if (substr_compare(strtolower($input), 'mail', -4) === 0) {
857
            $suffix = '';
858
        }
859
860
        /**
861
         * - Suffix is needed to avoid usage of reserved word.
862
         * - Str::slug will remove all forbidden characters.
863
         */
864
        $name = ucwords(Str::camel(Str::slug($input, '_'))).$suffix;
865
866
        if (! preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $name) ||
867
            substr_compare($name, $suffix, -strlen($suffix), strlen($suffix), true) !== 0
868
        ) {
869
            return false;
870
        }
871
872
        return $name;
873
    }
874
}
875