GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — integration ( a3ab80...7a98f7 )
by Brendan
06:22
created

PageManager::fetch()   C

Complexity

Conditions 9
Paths 64

Size

Total Lines 49
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 25
c 1
b 0
f 0
nc 64
nop 5
dl 0
loc 49
rs 5.7446
1
<?php
2
3
    /**
4
     * @package toolkit
5
     */
6
7
    /**
8
     * The `PageManager` class is responsible for providing basic CRUD operations
9
     * for Symphony frontend pages. These pages are stored in the database in
10
     * `tbl_pages` and are resolved to an instance of `FrontendPage` class from a URL.
11
     * Additionally, this manager provides functions to access the Page's types,
12
     * and any linked datasources or events.
13
     *
14
     * @since Symphony 2.3
15
     */
16
    class PageManager
17
    {
18
        /**
19
         * Given an associative array of data, where the key is the column name
20
         * in `tbl_pages` and the value is the data, this function will create a new
21
         * Page and return a Page ID on success.
22
         *
23
         * @param array $fields
24
         *  Associative array of field names => values for the Page
25
         * @throws DatabaseException
26
         * @return integer|boolean
27
         *  Returns the Page ID of the created Page on success, false otherwise.
28
         */
29 View Code Duplication
        public static function add(array $fields)
30
        {
31
            if (!isset($fields['sortorder'])) {
32
                $fields['sortorder'] = self::fetchNextSortOrder();
33
            }
34
35
            if (!Symphony::Database()->insert($fields, 'tbl_pages')) {
36
                return false;
37
            }
38
39
            return Symphony::Database()->getInsertID();
40
        }
41
42
        /**
43
         * Work out the next available sort order for a new page
44
         *
45
         * @return integer
46
         *  Returns the next sort order
47
         */
48
        public static function fetchNextSortOrder()
49
        {
50
            $next = Symphony::Database()->fetchVar(
51
                "next",
52
                0,
53
                "SELECT
54
                MAX(p.sortorder) + 1 AS `next`
55
            FROM
56
                `tbl_pages` AS p
57
            LIMIT 1"
58
            );
59
60
            return ($next ? (int)$next : 1);
61
        }
62
63
        /**
64
         * Return a Page title by the handle
65
         *
66
         * @param string $handle
67
         *  The handle of the page
68
         * @return integer
69
         *  The Page title
70
         */
71
        public static function fetchTitleFromHandle($handle)
72
        {
73
            return Symphony::Database()->fetchVar('title', 0, "
74
            SELECT `title`
75
            FROM `tbl_pages`
76
            WHERE `handle` = ?
77
            LIMIT 1",
78
                array($handle)
79
            );
80
        }
81
82
        /**
83
         * Return a Page ID by the handle
84
         *
85
         * @param string $handle
86
         *  The handle of the page
87
         * @return integer
88
         *  The Page ID
89
         */
90
        public static function fetchIDFromHandle($handle)
91
        {
92
            return Symphony::Database()->fetchVar('id', 0, "
93
            SELECT `id`
94
            FROM `tbl_pages`
95
            WHERE `handle` = ?
96
            LIMIT 1",
97
                array($handle)
98
            );
99
        }
100
101
        /**
102
         * Given a Page ID and an array of types, this function will add Page types
103
         * to that Page. If a Page types are stored in `tbl_pages_types`.
104
         *
105
         * @param integer $page_id
106
         *  The Page ID to add the Types to
107
         * @param array $types
108
         *  An array of page types
109
         * @throws DatabaseException
110
         * @return boolean
111
         */
112
        public static function addPageTypesToPage($page_id = null, array $types)
113
        {
114
            if (is_null($page_id)) {
115
                return false;
116
            }
117
118
            PageManager::deletePageTypes($page_id);
119
120
            foreach ($types as $type) {
121
                Symphony::Database()->insert(
122
                    array(
123
                        'page_id' => $page_id,
124
                        'type' => $type
125
                    ),
126
                    'tbl_pages_types'
127
                );
128
            }
129
130
            return true;
131
        }
132
133
        /**
134
         * Given a `$page_id`, this function will remove all associated
135
         * Page Types from `tbl_pages_types`.
136
         *
137
         * @param integer $page_id
138
         *  The ID of the Page that should be deleted.
139
         * @throws DatabaseException
140
         * @return boolean
141
         */
142
        public static function deletePageTypes($page_id = null)
143
        {
144
            if (is_null($page_id)) {
145
                return false;
146
            }
147
148
            return Symphony::Database()->delete('tbl_pages_types', "`page_id` = ?", array($page_id));
149
        }
150
151
        /**
152
         * This function will update all children of a particular page (if any)
153
         * by renaming/moving all related files to their new path and updating
154
         * their database information. This is a recursive function and will work
155
         * to any depth.
156
         *
157
         * @param integer $page_id
158
         *  The ID of the Page whose children need to be updated
159
         * @param string $page_path
160
         *  The path of the Page, which is the handles of the Page parents. If the
161
         *  page has multiple parents, they will be separated by a forward slash.
162
         *  eg. article/read. If a page has no parents, this parameter should be null.
163
         * @throws Exception
164
         * @return boolean
165
         */
166
        public static function editPageChildren($page_id = null, $page_path = null)
167
        {
168
            if (!is_int($page_id)) {
169
                return false;
170
            }
171
172
            $page_path = trim($page_path, '/');
173
            $children = PageManager::fetchChildPages($page_id);
174
175
            foreach ($children as $child) {
0 ignored issues
show
Bug introduced by
The expression $children of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
176
                $child_id = (int)$child['id'];
177
                $fields = array(
178
                    'path' => $page_path
179
                );
180
181
                if (!PageManager::createPageFiles($page_path, $child['handle'], $child['path'], $child['handle'])) {
182
                    $success = false;
0 ignored issues
show
Unused Code introduced by
$success is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
183
                }
184
185
                if (!PageManager::edit($child_id, $fields)) {
186
                    $success = false;
0 ignored issues
show
Unused Code introduced by
$success is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
187
                }
188
189
                $success = PageManager::editPageChildren($child_id, $page_path . '/' . $child['handle']);
190
            }
191
192
            return $success;
0 ignored issues
show
Bug introduced by
The variable $success does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
193
        }
194
195
        /**
196
         * Returns the child Pages (if any) of the given `$page_id`.
197
         *
198
         * @param integer $page_id
199
         *  The ID of the Page.
200
         * @param array $select (optional)
201
         *  Accepts an array of columns to return from `tbl_pages`. If omitted,
202
         *  all columns from the table will be returned.
203
         * @return array|null
204
         *  An associative array of Page information with the key being the column
205
         *  name from `tbl_pages` and the value being the data. If multiple Pages
206
         *  are found, an array of Pages will be returned. If no Pages are found
207
         *  null is returned.
208
         */
209
        public static function fetchChildPages($page_id = null, array $select = array())
210
        {
211
            if (is_null($page_id)) {
212
                return null;
213
            }
214
215
            if (empty($select)) {
216
                $select = array('*');
217
            }
218
219
            return PageManager::fetch(false, $select, array(
220
                sprintf('id != %d', $page_id),
221
                sprintf('parent = %d', $page_id)
222
            ));
223
        }
224
225
        /**
226
         * This function will return an associative array of Page information. The
227
         * information returned is defined by the `$include_types` and `$select`
228
         * parameters, which will return the Page Types for the Page and allow
229
         * a developer to restrict what information is returned about the Page.
230
         * Optionally, `$where` and `$order_by` parameters allow a developer to
231
         * further refine their query.
232
         *
233
         * @param boolean $include_types
234
         *  Whether to include the resulting Page's Page Types in the return array,
235
         *  under the key `type`. Defaults to true.
236
         * @param array $select (optional)
237
         *  Accepts an array of columns to return from `tbl_pages`. If omitted,
238
         *  all columns from the table will be returned.
239
         * @param array $where (optional)
240
         *  Accepts an array of WHERE statements that will be appended with AND.
241
         *  If omitted, all pages will be returned.
242
         * @param string $order_by (optional)
243
         *  Allows a developer to return the Pages in a particular order. The string
244
         *  passed will be appended to `ORDER BY`. If omitted this will return
245
         *  Pages ordered by `sortorder`.
246
         * @param boolean $hierarchical (optional)
247
         *  If true, builds a multidimensional array representing the pages hierarchy.
248
         *  Defaults to false.
249
         * @return array|null
250
         *  An associative array of Page information with the key being the column
251
         *  name from `tbl_pages` and the value being the data. If requested, the array
252
         *  can be made multidimensional to reflect the pages hierarchy. If no Pages are
253
         *  found, null is returned.
254
         */
255
        public static function fetch(
256
            $include_types = true,
257
            array $select = array(),
258
            array $where = array(),
259
            $order_by = null,
260
            $hierarchical = false
261
        ) {
262
            if ($hierarchical) {
263
                $select = array_merge($select, array('id', 'parent'));
264
            }
265
266
            if (empty($select)) {
267
                $select = array('*');
268
            }
269
270
            if (is_null($order_by)) {
271
                $order_by = 'sortorder ASC';
272
            }
273
274
            $pages = Symphony::Database()->fetch(sprintf(
275
                "SELECT
276
                %s
277
            FROM
278
                `tbl_pages` AS p
279
            WHERE
280
                %s
281
            ORDER BY
282
                %s",
283
                implode(',', $select),
284
                empty($where) ? '1' : implode(' AND ', $where),
285
                $order_by
286
            ));
287
288
            // Fetch the Page Types for each page, if required
289
            if ($include_types) {
290
                foreach ($pages as &$page) {
0 ignored issues
show
Bug introduced by
The expression $pages of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
291
                    $page['type'] = PageManager::fetchPageTypes($page['id']);
292
                }
293
            }
294
295
            if ($hierarchical) {
296
                $output = array();
297
298
                self::__buildTreeView(null, $pages, $output);
299
                $pages = $output;
300
            }
301
302
            return !empty($pages) ? $pages : array();
303
        }
304
305
        /**
306
         * This function returns a Page's Page Types. If the `$page_id`
307
         * parameter is given, the types returned will be for that Page.
308
         *
309
         * @param integer $page_id
310
         *  The ID of the Page.
311
         * @return array
312
         *  An array of the Page Types
313
         */
314
        public static function fetchPageTypes($page_id = null)
315
        {
316
            $where = '';
317
            $values = array();
318
319
            if (!is_null($page_id)) {
320
                $where = " WHERE pt.page_id = ? ";
321
                $values = array($page_id);
322
            }
323
324
            return Symphony::Database()->fetchCol('type', "
325
            SELECT
326
                type
327
            FROM
328
                `tbl_pages_types` AS pt "
329
                . $where . "
330
            GROUP BY
331
                pt.type
332
            ORDER BY
333
                pt.type ASC",
334
                $values
335
            );
336
        }
337
338
        private function __buildTreeView($parent_id, $pages, &$results)
339
        {
340
            if (!is_array($pages)) {
341
                return;
342
            }
343
344
            foreach ($pages as $page) {
345
                if ($page['parent'] === $parent_id) {
346
                    $results[] = $page;
347
348
                    self::__buildTreeView($page['id'], $pages, $results[count($results) - 1]['children']);
349
                }
350
            }
351
        }
352
353
        /**
354
         * This function creates the initial `.xsl` template for the page, whether
355
         * that be from the `TEMPLATES/blueprints.page.xsl` file, or from an existing
356
         * template with the same name. This function will handle the renaming of a page
357
         * by creating the new files using the old files as the templates then removing
358
         * the old template. If a template already exists for a Page, it will not
359
         * be overridden and the function will return true.
360
         *
361
         * @see toolkit.PageManager#resolvePageFileLocation()
362
         * @see toolkit.PageManager#createHandle()
363
         * @param string $new_path
364
         *  The path of the Page, which is the handles of the Page parents. If the
365
         *  page has multiple parents, they will be separated by a forward slash.
366
         *  eg. article/read. If a page has no parents, this parameter should be null.
367
         * @param string $new_handle
368
         *  The new Page handle, generated using `PageManager::createHandle`.
369
         * @param string $old_path (optional)
370
         *  This parameter is only required when renaming a Page. It should be the 'old
371
         *  path' before the Page was renamed.
372
         * @param string $old_handle (optional)
373
         *  This parameter is only required when renaming a Page. It should be the 'old
374
         *  handle' before the Page was renamed.
375
         * @throws Exception
376
         * @return boolean
377
         *  True when the page files have been created successfully, false otherwise.
378
         */
379
        public static function createPageFiles($new_path, $new_handle, $old_path = null, $old_handle = null)
380
        {
381
            $new = PageManager::resolvePageFileLocation($new_path, $new_handle);
382
            $old = PageManager::resolvePageFileLocation($old_path, $old_handle);
383
384
            // Nothing to do:
385
            if (file_exists($new) && $new === $old) {
386
                return true;
387
            }
388
389
            // Old file doesn't exist, use template:
390
            if (!file_exists($old)) {
391
                $data = file_get_contents(self::getTemplate('blueprints.page'));
392
            } else {
393
                $data = file_get_contents($old);
394
            }
395
396
            /**
397
             * Just before a Page Template is about to be created & written to disk
398
             *
399
             * @delegate PageTemplatePreCreate
400
             * @since Symphony 2.2.2
401
             * @param string $context
402
             * '/blueprints/pages/'
403
             * @param string $file
404
             *  The path to the Page Template file
405
             * @param string $contents
406
             *  The contents of the `$data`, passed by reference
407
             */
408
            Symphony::ExtensionManager()->notifyMembers('PageTemplatePreCreate', '/blueprints/pages/',
409
                array('file' => $new, 'contents' => &$data));
410
411
            if (PageManager::writePageFiles($new, $data)) {
412
                // Remove the old file, in the case of a rename
413
                if (file_exists($old)) {
414
                    General::deleteFile($old);
415
                }
416
417
                /**
418
                 * Just after a Page Template is saved after been created.
419
                 *
420
                 * @delegate PageTemplatePostCreate
421
                 * @since Symphony 2.2.2
422
                 * @param string $context
423
                 * '/blueprints/pages/'
424
                 * @param string $file
425
                 *  The path to the Page Template file
426
                 */
427
                Symphony::ExtensionManager()->notifyMembers('PageTemplatePostCreate', '/blueprints/pages/',
428
                    array('file' => $new));
429
430
                return true;
431
            }
432
433
            return false;
434
        }
435
436
        /**
437
         * Resolves the path to this page's XSLT file. The Symphony convention
438
         * is that they are stored in the `PAGES` folder. If this page has a parent
439
         * it will be as if all the / in the URL have been replaced with _. ie.
440
         * /articles/read/ will produce a file `articles_read.xsl`
441
         *
442
         * @see toolkit.PageManager#createFilePath()
443
         * @param string $path
444
         *  The URL path to this page, excluding the current page. ie, /articles/read
445
         *  would make `$path` become articles/
446
         * @param string $handle
447
         *  The handle of the page.
448
         * @return string
449
         *  The path to the XSLT of the page
450
         */
451
        public static function resolvePageFileLocation($path, $handle)
452
        {
453
            return PAGES . '/' . PageManager::createFilePath($path, $handle) . '.xsl';
454
        }
455
456
        /**
457
         * This function takes a `$path` and `$handle` and generates a flattened
458
         * string for use as a filename for a Page's template.
459
         *
460
         * @param string $path
461
         *  The path of the Page, which is the handles of the Page parents. If the
462
         *  page has multiple parents, they will be separated by a forward slash.
463
         *  eg. article/read. If a page has no parents, this parameter should be null.
464
         * @param string $handle
465
         *  A Page handle, generated using `PageManager::createHandle`.
466
         * @return string
467
         */
468
        public static function createFilePath($path, $handle)
469
        {
470
            return trim(str_replace('/', '_', $path . '_' . $handle), '_');
471
        }
472
473
        /**
474
         * Returns the path to the page-template by looking at the
475
         * `WORKSPACE/template/` directory, then at the `TEMPLATES`
476
         * directory for `$name.xsl`. If the template is not found,
477
         * false is returned
478
         *
479
         * @param string $name
480
         *  Name of the template
481
         * @return mixed
482
         *  String, which is the path to the template if the template is found,
483
         *  false otherwise
484
         */
485 View Code Duplication
        public static function getTemplate($name)
486
        {
487
            $format = '%s/%s.xsl';
488
489
            if (file_exists($template = sprintf($format, WORKSPACE . '/template', $name))) {
490
                return $template;
491
            } elseif (file_exists($template = sprintf($format, TEMPLATE, $name))) {
492
                return $template;
493
            } else {
494
                return false;
495
            }
496
        }
497
498
        /**
499
         * A wrapper for `General::writeFile`, this function takes a `$path`
500
         * and a `$data` and writes the new template to disk.
501
         *
502
         * @param string $path
503
         *  The path to write the template to
504
         * @param string $data
505
         *  The contents of the template
506
         * @return boolean
507
         *  True when written successfully, false otherwise
508
         */
509
        public static function writePageFiles($path, $data)
510
        {
511
            return General::writeFile($path, $data, Symphony::Configuration()->get('write_mode', 'file'));
0 ignored issues
show
Documentation introduced by
\Symphony::Configuration...t('write_mode', 'file') is of type array|string, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
512
        }
513
514
        /**
515
         * This function will update a Page in `tbl_pages` given a `$page_id`
516
         * and an associative array of `$fields`. A third parameter, `$delete_types`
517
         * will also delete the Page's associated Page Types if passed true.
518
         *
519
         * @see toolkit.PageManager#addPageTypesToPage()
520
         * @param integer $page_id
521
         *  The ID of the Page that should be updated
522
         * @param array $fields
523
         *  Associative array of field names => values for the Page.
524
         *  This array does need to contain every value for the Page, it
525
         *  can just be the changed values.
526
         * @param boolean $delete_types
527
         *  If true, this parameter will cause the Page Types of the Page to
528
         *  be deleted. By default this is false.
529
         * @return boolean
530
         */
531
        public static function edit($page_id, array $fields, $delete_types = false)
532
        {
533
            if (!is_numeric($page_id)) {
534
                return false;
535
            }
536
537
            if (isset($fields['id'])) {
538
                unset($fields['id']);
539
            }
540
541
            if (Symphony::Database()->update($fields, 'tbl_pages', ' `id` = ?', array($page_id))) {
542
                // If set, this will clear the page's types.
543
                if ($delete_types) {
544
                    PageManager::deletePageTypes($page_id);
545
                }
546
547
                return true;
548
            } else {
549
                return false;
550
            }
551
        }
552
553
        /**
554
         * This function takes a Page ID and removes the Page from the database
555
         * in `tbl_pages` and it's associated Page Types in `tbl_pages_types`.
556
         * This function does not delete any of the Page's children.
557
         *
558
         * @see toolkit.PageManager#deletePageTypes
559
         * @see toolkit.PageManager#deletePageFiles
560
         * @param integer $page_id
561
         *  The ID of the Page that should be deleted.
562
         * @param boolean $delete_files
563
         *  If true, this parameter will remove the Page's templates from the
564
         *  the filesystem. By default this is true.
565
         * @throws DatabaseException
566
         * @throws Exception
567
         * @return boolean
568
         */
569
        public static function delete($page_id = null, $delete_files = true)
570
        {
571
            if (!is_int($page_id)) {
572
                return false;
573
            }
574
575
            $can_proceed = true;
576
577
            // Delete Files (if told to)
578
            if ($delete_files) {
579
                $page = PageManager::fetchPageByID($page_id, array('path', 'handle'));
580
581
                if (empty($page)) {
582
                    return false;
583
                }
584
585
                $can_proceed = PageManager::deletePageFiles($page['path'], $page['handle']);
586
            }
587
588
            // Delete from tbl_pages/tbl_page_types
589
            if ($can_proceed) {
590
                PageManager::deletePageTypes($page_id);
591
                Symphony::Database()->delete('tbl_pages', "`id` = ? ", array($page_id));
592
                Symphony::Database()->query(sprintf(
593
                    "UPDATE
594
                    tbl_pages
595
                SET
596
                    `sortorder` = (`sortorder` + 1)
597
                WHERE
598
                    `sortorder` < %d",
599
                    $page_id
600
                ));
601
            }
602
603
            return $can_proceed;
604
        }
605
606
        /**
607
         * Returns Pages that match the given `$page_id`. Developers can optionally
608
         * choose to specify what Page information is returned using the `$select`
609
         * parameter.
610
         *
611
         * @param integer|array $page_id
612
         *  The ID of the Page, or an array of ID's
613
         * @param array $select (optional)
614
         *  Accepts an array of columns to return from `tbl_pages`. If omitted,
615
         *  all columns from the table will be returned.
616
         * @return array|null
617
         *  An associative array of Page information with the key being the column
618
         *  name from `tbl_pages` and the value being the data. If multiple Pages
619
         *  are found, an array of Pages will be returned. If no Pages are found
620
         *  null is returned.
621
         */
622
        public static function fetchPageByID($page_id = null, array $select = array())
623
        {
624
            if (is_null($page_id)) {
625
                return null;
626
            }
627
628
            if (!is_array($page_id)) {
629
                $page_id = array(
630
                    Symphony::Database()->cleanValue($page_id)
631
                );
632
            }
633
634
            if (empty($select)) {
635
                $select = array('*');
636
            }
637
638
            $page = PageManager::fetch(true, $select, array(
639
                sprintf("id IN (%s)", implode(',', $page_id))
640
            ));
641
642
            return count($page) === 1 ? array_pop($page) : $page;
643
        }
644
645
        /**
646
         * Given a Page's `$path` and `$handle`, this function will remove
647
         * it's templates from the `PAGES` directory returning boolean on
648
         * completion
649
         *
650
         * @param string $page_path
651
         *  The path of the Page, which is the handles of the Page parents. If the
652
         *  page has multiple parents, they will be separated by a forward slash.
653
         *  eg. article/read. If a page has no parents, this parameter should be null.
654
         * @param string $handle
655
         *  A Page handle, generated using `PageManager::createHandle`.
656
         * @throws Exception
657
         * @return boolean
658
         */
659
        public static function deletePageFiles($page_path, $handle)
660
        {
661
            $file = PageManager::resolvePageFileLocation($page_path, $handle);
662
663
            // Nothing to do:
664
            if (!file_exists($file)) {
665
                return true;
666
            }
667
668
            // Delete it:
669
            if (General::deleteFile($file)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return \General::deleteFile($file);.
Loading history...
670
                return true;
671
            }
672
673
            return false;
674
        }
675
676
        /**
677
         * Returns Pages that match the given `$type`. If no `$type` is provided
678
         * the function returns the result of `PageManager::fetch`.
679
         *
680
         * @param string $type
681
         *  Where the type is one of the available Page Types.
682
         * @return array|null
683
         *  An associative array of Page information with the key being the column
684
         *  name from `tbl_pages` and the value being the data. If multiple Pages
685
         *  are found, an array of Pages will be returned. If no Pages are found
686
         *  null is returned.
687
         */
688
        public static function fetchPageByType($type = null)
689
        {
690
            if (is_null($type)) {
691
                return PageManager::fetch();
692
            }
693
694
            $pages = Symphony::Database()->fetch(sprintf(
695
                "SELECT
696
                `p`.*
697
            FROM
698
                `tbl_pages` AS `p`
699
            LEFT JOIN
700
                `tbl_pages_types` AS `pt` ON (p.id = pt.page_id)
701
            WHERE
702
                `pt`.type = '%s'",
703
                Symphony::Database()->cleanValue($type)
704
            ));
705
706
            return count($pages) === 1 ? array_pop($pages) : $pages;
707
        }
708
709
        /**
710
         * Returns all the page types that exist in this Symphony install.
711
         * There are 6 default system page types, and new types can be added
712
         * by Developers via the Page Editor.
713
         *
714
         * @since Symphony 2.3 introduced the JSON type.
715
         * @return array
716
         *  An array of strings of the page types used in this Symphony
717
         *  install. At the minimum, this will be an array with the values
718
         * 'index', 'XML', 'JSON', 'admin', '404' and '403'.
719
         */
720
        public static function fetchAvailablePageTypes()
721
        {
722
            $system_types = array('index', 'XML', 'JSON', 'admin', '404', '403');
723
724
            $types = PageManager::fetchPageTypes();
725
726
            return (!empty($types) ? General::array_remove_duplicates(array_merge($system_types,
727
                $types)) : $system_types);
728
        }
729
730
        /**
731
         * Fetch an associated array with Page ID's and the types they're using.
732
         *
733
         * @throws DatabaseException
734
         * @return array
735
         *  A 2-dimensional associated array where the key is the page ID.
736
         */
737
        public static function fetchAllPagesPageTypes()
738
        {
739
            $types = Symphony::Database()->fetch("SELECT `page_id`,`type` FROM `tbl_pages_types`");
740
            $page_types = array();
741
742
            if (is_array($types)) {
743
                foreach ($types as $type) {
744
                    $page_types[$type['page_id']][] = $type['type'];
745
                }
746
            }
747
748
            return $page_types;
749
        }
750
751
        /**
752
         * Given a name, this function will return a page handle. These handles
753
         * will only contain latin characters
754
         *
755
         * @param string $name
756
         *  The Page name to generate a handle for
757
         * @return string
758
         */
759
        public static function createHandle($name)
760
        {
761
            return Lang::createHandle($name, 255, '-', false, true, array(
762
                '@^[^a-z\d]+@i' => '',
763
                '/[^\w-\.]/i' => ''
764
            ));
765
        }
766
767
        /**
768
         * This function will return the number of child pages for a given
769
         * `$page_id`. This is a recursive function and will return the absolute
770
         * count.
771
         *
772
         * @param integer $page_id
773
         *  The ID of the Page.
774
         * @return integer
775
         *  The number of child pages for the given `$page_id`
776
         */
777
        public static function getChildPagesCount($page_id = null)
778
        {
779
            if (is_null($page_id)) {
780
                return null;
781
            }
782
783
            $children = PageManager::fetch(false, array('id'), array(
784
                sprintf('parent = %d', $page_id)
785
            ));
786
            $count = count($children);
787
788
            if ($count > 0) {
789
                foreach ($children as $c) {
0 ignored issues
show
Bug introduced by
The expression $children of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
790
                    $count += self::getChildPagesCount($c['id']);
791
                }
792
            }
793
794
            return $count;
795
        }
796
797
        /**
798
         * Returns boolean if a the given `$type` has been used by Symphony
799
         * for a Page that is not `$page_id`.
800
         *
801
         * @param integer $page_id
802
         *  The ID of the Page to exclude from the query.
803
         * @param string $type
804
         *  The Page Type to look for in `tbl_page_types`.
805
         * @return boolean
806
         *  True if the type is used, false otherwise
807
         */
808
        public static function hasPageTypeBeenUsed($page_id = null, $type)
809
        {
810
            return (boolean)Symphony::Database()->fetchRow(0, "
811
                SELECT
812
                    pt.id
813
                FROM
814
                    `tbl_pages_types` AS pt
815
                WHERE
816
                    pt.page_id != ?
817
                    AND pt.type = ?
818
                LIMIT 1
819
            ",
820
                array(
821
                    $page_id,
822
                    $type
823
                )
824
            );
825
        }
826
827
        /**
828
         * Given a `$page_id`, this function returns boolean if the page
829
         * has child pages.
830
         *
831
         * @param integer $page_id
832
         *  The ID of the Page to check
833
         * @return boolean
834
         *  True if the page has children, false otherwise
835
         */
836
        public static function hasChildPages($page_id = null)
837
        {
838
            return (boolean)Symphony::Database()->fetchVar('id', 0, "
839
                SELECT
840
                    p.id
841
                FROM
842
                    `tbl_pages` AS p
843
                WHERE
844
                    p.parent = ?
845
                LIMIT 1
846
            ",
847
                array($page_id)
848
            );
849
        }
850
851
        /**
852
         * Given the `$page_id`, return the complete title of the
853
         * current page. Each part of the Page's title will be
854
         * separated by ': '.
855
         *
856
         * @param mixed $page_id
857
         *  The ID of the Page that currently being viewed, or the handle of the
858
         *  current Page
859
         * @return string
860
         *  The title of the current Page. If the page is a child of another
861
         *  it will be prepended by the parent and a colon, ie. Articles: Read
862
         */
863
        public static function resolvePageTitle($page_id)
864
        {
865
            $path = PageManager::resolvePage($page_id, 'title');
866
867
            return implode(': ', $path);
868
        }
869
870
        /**
871
         * Given the `$page_id` and a `$column`, this function will return an
872
         * array of the given `$column` for the Page, including all parents.
873
         *
874
         * @param mixed $page_id
875
         *  The ID of the Page that currently being viewed, or the handle of the
876
         *  current Page
877
         * @param string $column
878
         * @return array
879
         *  An array of the current Page, containing the `$column`
880
         *  requested. The current page will be the last item the array, as all
881
         *  parent pages are prepended to the start of the array
882
         */
883
        public static function resolvePage($page_id, $column)
884
        {
885
            $page = Symphony::Database()->fetchRow(0, sprintf("
886
                SELECT
887
                    p.%s,
888
                    p.parent
889
                FROM
890
                    `tbl_pages` AS p
891
                WHERE
892
                    p.id = ?
893
                    OR p.handle = ?
894
                LIMIT 1",
895
                $column
896
            ),
897
                array(
898
                    $page_id,
899
                    $page_id
900
                )
901
            );
902
903
            if (empty($page)) {
904
                return $page;
905
            }
906
907
            $path = array($page[$column]);
908
909
            if (!is_null($page['parent'])) {
910
                $next_parent = $page['parent'];
911
912
                while (
913
                $parent = Symphony::Database()->fetchRow(0, sprintf("
914
                        SELECT
915
                            p.%s,
916
                            p.parent
917
                        FROM
918
                            `tbl_pages` AS p
919
                        WHERE
920
                            p.id = ?
921
                        ",
922
                    $column
923
                ),
924
                    array($next_parent)
925
                )
926
                ) {
927
                    array_unshift($path, $parent[$column]);
928
                    $next_parent = $parent['parent'];
929
                }
930
            }
931
932
            return $path;
933
        }
934
935
        /**
936
         * Given the `$page_id`, return the complete path to the
937
         * current page. Each part of the Page's path will be
938
         * separated by '/'.
939
         *
940
         * @param mixed $page_id
941
         *  The ID of the Page that currently being viewed, or the handle of the
942
         *  current Page
943
         * @return string
944
         *  The complete path to the current Page including any parent
945
         *  Pages, ie. /articles/read
946
         */
947
        public static function resolvePagePath($page_id)
948
        {
949
            $path = PageManager::resolvePage($page_id, 'handle');
950
951
            return implode('/', $path);
952
        }
953
954
        /**
955
         * Resolve a page by it's handle and path
956
         *
957
         * @param string $handle
958
         *  The handle of the page
959
         * @param boolean $path
960
         *  The path to the page
961
         * @return array|boolean
962
         *  Array if found, false if not
963
         */
964
        public static function resolvePageByPath($handle, $path = false)
965
        {
966
            $values = array();
967
            if ($path) {
968
                $where = " `path` = ? AND ";
969
                $values[] = $path;
970
            } else {
971
                $where = " `path` IS NULL AND ";
972
            }
973
974
            $where .= " `handle` = ?";
975
            $values[] = $handle;
976
977
            return Symphony::Database()->fetchRow(0, "SELECT * FROM `tbl_pages` WHERE " . $where . " LIMIT 1", $values);
978
        }
979
980
        /**
981
         * Check whether a datasource is used or not
982
         *
983
         * @param string $handle
984
         *  The datasource handle
985
         * @return boolean
986
         *  True if used, false if not
987
         */
988
        public static function isDataSourceUsed($handle)
989
        {
990
            return (boolean)Symphony::Database()->fetchVar('count', 0,
991
                "SELECT COUNT(*) AS `count` FROM `tbl_pages` WHERE `data_sources` REGEXP '[[:<:]]{$handle}[[:>:]]' ") > 0;
992
        }
993
994
        /**
995
         * Check whether a event is used or not
996
         *
997
         * @param string $handle
998
         *  The event handle
999
         * @return boolean
1000
         *  True if used, false if not
1001
         */
1002
        public static function isEventUsed($handle)
1003
        {
1004
            return (boolean)Symphony::Database()->fetchVar('count', 0,
1005
                "SELECT COUNT(*) AS `count` FROM `tbl_pages` WHERE `events` REGEXP '[[:<:]]{$handle}[[:>:]]' ") > 0;
1006
        }
1007
    }
1008