Test Failed
Push — master ( dc68d1...1f5199 )
by Mathieu
02:31
created

SectionTableTrait::abridgeStr()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 5
nop 4
1
<?php
2
3
namespace Charcoal\Admin\Widget\Cms;
4
5
use Exception;
6
use RuntimeException;
7
8
// From 'charcoal-core'
9
use Charcoal\Model\ModelInterface;
10
11
// From 'charcoal-factory'
12
use Charcoal\Factory\FactoryInterface;
13
14
// From 'charcoal-property'
15
use Charcoal\Property\PropertyInterface;
16
17
// Local dependency
18
use Charcoal\Cms\AbstractSection;
19
20
/**
21
 *
22
 */
23
trait SectionTableTrait
24
{
25
    /**
26
     * Store the factory instance for the current class.
27
     *
28
     * @var FactoryInterface
29
     */
30
    private $sectionFactory;
31
32
    /**
33
     * Set an object model factory.
34
     *
35
     * @param FactoryInterface $factory The model factory, to create objects.
36
     * @return self
37
     */
38
    protected function setSectionFactory(FactoryInterface $factory)
39
    {
40
        $this->sectionFactory = $factory;
41
42
        return $this;
43
    }
44
45
    /**
46
     * Retrieve the object model factory.
47
     *
48
     * @throws RuntimeException If the model factory was not previously set.
49
     * @return FactoryInterface
50
     */
51
    public function sectionFactory()
52
    {
53
        if (!isset($this->sectionFactory)) {
54
            throw new RuntimeException(
55
                sprintf('Section Model Factory is not defined for "%s"', get_class($this))
56
            );
57
        }
58
59
        return $this->sectionFactory;
60
    }
61
62
    /**
63
     * Filter the property before its assigned to the object row.
64
     *
65
     * This method is useful for classes using this trait.
66
     *
67
     * @param  ModelInterface    $object        The current row's object.
68
     * @param  PropertyInterface $property      The current property.
69
     * @param  string            $propertyValue The property $key's display value.
70
     * @return array
71
     */
72
    protected function parsePropertyCell(
73
        ModelInterface $object,
74
        PropertyInterface $property,
75
        $propertyValue
76
    ) {
77
        $propertyIdent = $property->ident();
78
79
        switch ($propertyIdent) {
80
            case 'template_ident':
81
                if ($object->templateIdent() === 'volleyball/template/page') {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Charcoal\Model\ModelInterface as the method templateIdent() does only exist in the following implementations of said interface: Charcoal\Attachment\Object\Accordion, Charcoal\Attachment\Object\Attachment, Charcoal\Attachment\Object\Category\Generic, Charcoal\Attachment\Object\Container, Charcoal\Attachment\Object\Embed, Charcoal\Attachment\Object\File, Charcoal\Attachment\Object\Gallery, Charcoal\Attachment\Object\Image, Charcoal\Attachment\Object\Join, Charcoal\Attachment\Object\Link, Charcoal\Attachment\Object\Text, Charcoal\Attachment\Object\Video, Charcoal\Cms\AbstractDocument, Charcoal\Cms\AbstractEvent, Charcoal\Cms\AbstractFaq, Charcoal\Cms\AbstractImage, Charcoal\Cms\AbstractNews, Charcoal\Cms\AbstractSection, Charcoal\Cms\AbstractText, Charcoal\Cms\AbstractVideo, Charcoal\Cms\Config, Charcoal\Cms\Document, Charcoal\Cms\DocumentCategory, Charcoal\Cms\EmptySection, Charcoal\Cms\Event, Charcoal\Cms\EventCategory, Charcoal\Cms\ExternalSection, Charcoal\Cms\Faq, Charcoal\Cms\FaqCategory, Charcoal\Cms\Image, Charcoal\Cms\ImageCategory, Charcoal\Cms\News, Charcoal\Cms\NewsCategory, Charcoal\Cms\Section, Charcoal\Cms\Section\BlocksSection, Charcoal\Cms\Section\ContentSection, Charcoal\Cms\Tag, Charcoal\Cms\Text, Charcoal\Cms\TextCategory, Charcoal\Cms\Video, Charcoal\Cms\VideoCategory, Charcoal\Model\AbstractModel, Charcoal\Model\Model, Charcoal\Object\Content, Charcoal\Object\ObjectRevision, Charcoal\Object\ObjectRoute, Charcoal\Object\ObjectSchedule, Charcoal\Object\Tests\Mocks\RoutableClass, Charcoal\Object\UserData.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
82
                    $propertyValue = sprintf(
83
                        '<span aria-hidden="true">─</span><span class="sr-only">%s</span>',
84
                        $this->translator()->translation([
0 ignored issues
show
Bug introduced by
It seems like translator() 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...
85
                            'en' => 'Default Template',
86
                            'fr' => 'Modèle par défaut'
87
                        ])
88
                    );
89
                }
90
                break;
91
92
            case 'title':
93
            case 'menu_label':
94
                $sectionTag = null;
0 ignored issues
show
Unused Code introduced by
$sectionTag 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...
95
                $sectionType = $object->sectionType();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Charcoal\Model\ModelInterface as the method sectionType() does only exist in the following implementations of said interface: Charcoal\Cms\AbstractSection, Charcoal\Cms\EmptySection, Charcoal\Cms\ExternalSection, Charcoal\Cms\Section, Charcoal\Cms\Section\BlocksSection, Charcoal\Cms\Section\ContentSection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
96
97
                switch ($sectionType) {
98
                    case AbstractSection::TYPE_EXTERNAL:
99
                        $externalUrl = (string)$object->externalUrl();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Charcoal\Model\ModelInterface as the method externalUrl() does only exist in the following implementations of said interface: Charcoal\Cms\AbstractSection, Charcoal\Cms\EmptySection, Charcoal\Cms\ExternalSection, Charcoal\Cms\Section, Charcoal\Cms\Section\BlocksSection, Charcoal\Cms\Section\ContentSection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
100
                        $linkExcerpt = '';
101
                        $tagTemplate = '<span class="glyphicon glyphicon-link" data-toggle="tooltip" '.
102
                            'data-placement="auto" title="%1$s"></span>';
103
104
                        if ($externalUrl) {
105
                            $tagTemplate = '<a class="btn btn-default btn-xs" href="%2$s" target="_blank">'.
106
                                '<span class="glyphicon glyphicon-link" aria-hidden="true"></span> '.
107
                                '<span class="sr-only">URL:</span> %3$s'.
108
                                '</a>';
109
110
                            $linkExcerpt = $this->abridgeUri($externalUrl);
111
                        }
112
113
                        $p = $object->p('section_type');
114
                        $propertyValue .= sprintf(
115
                            ' &nbsp; '.$tagTemplate,
116
                            $p->displayVal($p->val()),
117
                            $externalUrl,
118
                            $linkExcerpt
119
                        );
120
                        break;
121
                }
122
                break;
123
        }
124
125
        if ($propertyIdent === 'title') {
126
            if (is_callable([ $object, 'navMenu' ]) && $object->navMenu()) {
0 ignored issues
show
Bug introduced by
The method navMenu() does not seem to exist on object<Charcoal\Model\ModelInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
127
                $propertyValue .= sprintf(
128
                    ' &nbsp; '.
129
                    '<span class="glyphicon glyphicon-list" data-toggle="tooltip" '.
130
                    'data-placement="auto" title="%s"></span>',
131
                    $this->translator()->translation([
0 ignored issues
show
Bug introduced by
It seems like translator() 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...
132
                        'en' => 'Present in a menu',
133
                        'fr' => 'Présent dans un menu'
134
                    ])
135
                );
136
            }
137
138
            if (is_callable([ $object, 'locked' ]) && $object->locked()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Charcoal\Model\ModelInterface as the method locked() does only exist in the following implementations of said interface: Charcoal\Cms\AbstractSection, Charcoal\Cms\EmptySection, Charcoal\Cms\ExternalSection, Charcoal\Cms\Section, Charcoal\Cms\Section\BlocksSection, Charcoal\Cms\Section\ContentSection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
139
                $propertyValue .= sprintf(
140
                    ' &nbsp; '.
141
                    '<span class="glyphicon glyphicon-lock" data-toggle="tooltip" '.
142
                    'data-placement="auto" title="%s"></span>',
143
                    $object->p('locked')->label()
144
                );
145
            }
146
        }
147
148
        return parent::parsePropertyCell($object, $property, $propertyValue);
149
    }
150
151
    /**
152
     * Retrieve an abridged variant to the given URI.
153
     *
154
     * @param string $uri A URI to possibly truncate.
155
     * @return string
156
     */
157
    private function abridgeUri($uri)
158
    {
159
        $i = 30;
160
        $j = 12;
161
162
        if (mb_strlen($uri) <= $i) {
163
            return $uri;
164
        }
165
166
        $host = rtrim(parse_url($uri, PHP_URL_HOST), '/');
167
        $path = '/'.ltrim(parse_url($uri, PHP_URL_PATH), '/');
168
169
        if ($host === getenv('HTTP_HOST')) {
170
            $i = 50;
171
            $j = 22;
172
173
            $host = '';
174
        } else {
175
            if (mb_strlen($host) > $i && mb_strlen($path) > $i) {
176
                return $this->abridgeStr($uri, 50, 22);
177
            }
178
179
            $host = $this->abridgeStr($host, 20, 7);
180
        }
181
182
        $path = $this->abridgeStr($path, $i, $j);
183
184
        return $host.$path;
185
    }
186
187
    /**
188
     * Retrieve an abridged variant to the given URI.
189
     *
190
     * @param string  $str The string to possibly truncate.
191
     * @param integer $l   Optional. The soft-limit of the string.
192
     * @param integer $a   Optional. The hard-limit to keep from the beginning of the string.
193
     * @param integer $z   Optional. The hard-limit to keep from the end of the string.
194
     * @return string
195
     */
196
    private function abridgeStr($str, $l = 30, $a = 12, $z = 12)
197
    {
198
        if (mb_strlen($str) > $l) {
199
            $str = (($a > 0) ? mb_substr($str, 0, $a) : '').'&hellip;'.(($z > 0) ? mb_substr($str, -$z) : '');
200
        }
201
202
        return $str;
203
    }
204
}
205