Completed
Push — master ( b82aa8...9291f8 )
by Vladimir
02:16
created

PermalinkDocumentTrait   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 97.56%

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 2
dl 0
loc 134
ccs 40
cts 41
cp 0.9756
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A handleSpecialRedirects() 0 3 1
A getTargetFile() 0 17 3
A getPermalink() 0 10 1
A getRedirects() 0 11 2
A getPathPermalink() 0 24 3
A sanitizePermalink() 0 27 3
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Document;
9
10
use allejo\stakx\Filesystem\FilesystemLoader as fs;
11
use allejo\stakx\RuntimeStatus;
12
use allejo\stakx\Service;
13
14
trait PermalinkDocumentTrait
15
{
16
    /** @var string */
17
    protected $permalink = null;
18
19
    /** @var array */
20
    protected $redirects = null;
21
22
    /** Extensions that need to be stripped from permalinks. */
23
    private static $extensionsToStrip = ['twig'];
24
25
    /**
26
     * {@inheritdoc}
27
     */
28 11
    public function handleSpecialRedirects()
29
    {
30 11
    }
31
32
    /**
33
     * {@inheritdoc}
34
     */
35 18
    final public function getTargetFile($permalink = null)
36
    {
37 18
        if ($permalink === null)
38
        {
39 16
            $permalink = $this->getPermalink();
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
40
        }
41
42 18
        $missingFile = (substr($permalink, -1) == '/');
43 18
        $permalink = str_replace('/', DIRECTORY_SEPARATOR, $permalink);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
44
45 18
        if ($missingFile)
46
        {
47 10
            $permalink = rtrim($permalink, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'index.html';
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
48
        }
49
50 18
        return ltrim($permalink, DIRECTORY_SEPARATOR);
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56 35
    final public function getPermalink()
57
    {
58 35
        $this->buildPermalink();
0 ignored issues
show
Bug introduced by
It seems like buildPermalink() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
59
60 35
        $this->permalink = $this->sanitizePermalink($this->permalink);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->sanitizePermalink($this->permalink) can also be of type array<integer,string>. However, the property $permalink is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
61 35
        $this->permalink = str_replace(DIRECTORY_SEPARATOR, '/', $this->permalink);
62 35
        $this->permalink = '/' . ltrim($this->permalink, '/'); // Permalinks should always use '/' and not be OS specific
63
64 35
        return $this->permalink;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 12
    final public function getRedirects()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
71
    {
72 12
        if ($this->redirects === null)
73
        {
74 1
            $this->getPermalink();
75
        }
76
77 12
        $this->handleSpecialRedirects();
78
79 12
        return $this->redirects;
80
    }
81
82
    /**
83
     * Get the permalink based off the location of where the file is relative to the website. This permalink is to be
84
     * used as a fallback in the case that a permalink is not explicitly specified in the Front Matter.
85
     *
86
     * @return string
87
     */
88 2
    final protected function getPathPermalink()
89
    {
90
        // Remove the protocol of the path, if there is one and prepend a '/' to the beginning
91 2
        $cleanPath = preg_replace('/[\w|\d]+:\/\//', '', $this->getRelativeFilePath());
0 ignored issues
show
Bug introduced by
It seems like getRelativeFilePath() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
92 2
        $cleanPath = ltrim($cleanPath, DIRECTORY_SEPARATOR);
93
94
        // Handle vfs:// paths by replacing their forward slashes with the OS appropriate directory separator
95 2
        if (DIRECTORY_SEPARATOR !== '/')
96
        {
97
            $cleanPath = str_replace('/', DIRECTORY_SEPARATOR, $cleanPath);
98
        }
99
100
        // Check the first folder and see if it's a data folder (starts with an underscore) intended for stakx
101 2
        $folders = explode(DIRECTORY_SEPARATOR, $cleanPath);
102
103 2
        if (substr($folders[0], 0, 1) === '_')
104
        {
105 1
            array_shift($folders);
106
        }
107
108 2
        $cleanPath = implode(DIRECTORY_SEPARATOR, $folders);
109
110 2
        return $cleanPath;
111
    }
112
113
    /**
114
     * Sanitize a permalink to remove unsupported characters or multiple '/' and replace spaces with hyphens.
115
     *
116
     * @param string $permalink A permalink
117
     *
118
     * @return string $permalink The sanitized permalink
0 ignored issues
show
Documentation introduced by
Should the return type not be string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
119
     */
120 35
    final private function sanitizePermalink($permalink)
121
    {
122
        // Remove multiple '/' together
123 35
        $permalink = preg_replace('/\/+/', '/', $permalink);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
124
125
        // Replace all spaces with hyphens
126 35
        $permalink = str_replace(' ', '-', $permalink);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
127
128
        // Remove all disallowed characters
129 35
        $permalink = preg_replace('/[^0-9a-zA-Z-_\/\\\.]/', '', $permalink);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
130
131 35
        if (in_array(fs::getExtension($permalink), self::$extensionsToStrip))
0 ignored issues
show
Documentation introduced by
$permalink is of type string, but the function expects a object<string>.

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...
132
        {
133 3
            $permalink = fs::removeExtension($permalink);
0 ignored issues
show
Documentation introduced by
$permalink is of type string, but the function expects a object<string>.

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...
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
134
        }
135
136
        // Remove any special characters before a sane value
137 35
        $permalink = preg_replace('/^[^0-9a-zA-Z-_]*/', '', $permalink);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
138
139
        // Convert permalinks to lower case
140 35
        if (!Service::hasRunTimeFlag(RuntimeStatus::COMPILER_PRESERVE_CASE))
141
        {
142 35
            $permalink = mb_strtolower($permalink, 'UTF-8');
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $permalink. This often makes code more readable.
Loading history...
143
        }
144
145 35
        return $permalink;
146
    }
147
}
148