Completed
Push — master ( ab2e78...f54036 )
by El
12:21
created

Paste   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 298
Duplicated Lines 10.07 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 96.26%

Importance

Changes 0
Metric Value
wmc 48
lcom 1
cbo 6
dl 30
loc 298
ccs 103
cts 107
cp 0.9626
rs 8.4864
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setExpiration() 0 13 3
A setBurnafterreading() 0 12 3
C get() 0 43 11
A store() 0 20 3
A delete() 0 4 1
A exists() 0 4 1
A getComment() 0 13 3
A getComments() 0 4 1
A getDeleteToken() 0 11 3
A setAttachment() 7 7 3
A setAttachmentName() 7 7 3
B setOpendiscussion() 0 15 5
A setFormatter() 0 7 2
A isBurnafterreading() 8 8 3
A isOpendiscussion() 8 8 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Paste often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Paste, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1
11
 */
12
13
namespace PrivateBin\Model;
14
15
use Exception;
16
use PrivateBin\Persistence\ServerSalt;
17
use PrivateBin\PrivateBin;
18
use PrivateBin\Sjcl;
19
20
/**
21
 * Paste
22
 *
23
 * Model of a PrivateBin paste.
24
 */
25
class Paste extends AbstractModel
26
{
27
    /**
28
     * Get paste data.
29
     *
30
     * @access public
31
     * @throws Exception
32
     * @return stdClass
33
     */
34 40
    public function get()
35
    {
36 40
        $data = $this->_store->read($this->getId());
37 40
        if ($data === false) {
38 1
            throw new Exception(PrivateBin::GENERIC_ERROR, 64);
39
        }
40
41
        // check if paste has expired and delete it if neccessary.
42 39
        if (property_exists($data->meta, 'expire_date')) {
43 5
            if ($data->meta->expire_date < time()) {
44 4
                $this->delete();
45 4
                throw new Exception(PrivateBin::GENERIC_ERROR, 63);
46
            }
47
            // We kindly provide the remaining time before expiration (in seconds)
48 1
            $data->meta->remaining_time = $data->meta->expire_date - time();
49
        }
50
51
        // check if non-expired burn after reading paste needs to be deleted
52 35
        if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading && $this->_conf->getKey('instantburnafterreading')) {
53 2
            $this->delete();
54
        }
55
56
        // set formatter for for the view.
57 35
        if (!property_exists($data->meta, 'formatter')) {
58
            // support < 0.21 syntax highlighting
59 6
            if (property_exists($data->meta, 'syntaxcoloring') && $data->meta->syntaxcoloring === true) {
60 2
                $data->meta->formatter = 'syntaxhighlighting';
61
            } else {
62 4
                $data->meta->formatter = $this->_conf->getKey('defaultformatter');
63
            }
64
        }
65
66
        // support old paste format with server wide salt
67 35
        if (!property_exists($data->meta, 'salt')) {
68 4
            $data->meta->salt = ServerSalt::get();
69
        }
70 35
        $data->comments       = array_values($this->getComments());
71 35
        $data->comment_count  = count($data->comments);
72 35
        $data->comment_offset = 0;
73 35
        $data->{'@context'}   = 'js/paste.jsonld';
74 35
        $this->_data          = $data;
75 35
        return $this->_data;
76
    }
77
78
    /**
79
     * Store the paste's data.
80
     *
81
     * @access public
82
     * @throws Exception
83
     */
84 35
    public function store()
85
    {
86
        // Check for improbable collision.
87 35
        if ($this->exists()) {
88 3
            throw new Exception('You are unlucky. Try again.', 75);
89
        }
90
91 33
        $this->_data->meta->postdate = time();
92 33
        $this->_data->meta->salt     = serversalt::generate();
93
94
        // store paste
95
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96 33
            $this->_store->create(
97 33
                $this->getId(),
98 33
                json_decode(json_encode($this->_data), true)
99 33
            ) === false
100
        ) {
101
            throw new Exception('Error saving paste. Sorry.', 76);
102
        }
103 33
    }
104
105
    /**
106
     * Delete the paste.
107
     *
108
     * @access public
109
     * @throws Exception
110
     */
111 26
    public function delete()
112
    {
113 26
        $this->_store->delete($this->getId());
114 26
    }
115
116
    /**
117
     * Test if paste exists in store.
118
     *
119
     * @access public
120
     * @return bool
121
     */
122 84
    public function exists()
123
    {
124 84
        return $this->_store->exists($this->getId());
125
    }
126
127
    /**
128
     * Get a comment, optionally a specific instance.
129
     *
130
     * @access public
131
     * @param string $parentId
132
     * @param string $commentId
133
     * @throws Exception
134
     * @return Comment
135
     */
136 20
    public function getComment($parentId, $commentId = null)
137
    {
138 20
        if (!$this->exists()) {
139 1
            throw new Exception('Invalid data.', 62);
140
        }
141 19
        $comment = new Comment($this->_conf, $this->_store);
142 19
        $comment->setPaste($this);
143 19
        $comment->setParentId($parentId);
144 17
        if ($commentId !== null) {
145 5
            $comment->setId($commentId);
146
        }
147 17
        return $comment;
148
    }
149
150
    /**
151
     * Get all comments, if any.
152
     *
153
     * @access public
154
     * @return array
155
     */
156 35
    public function getComments()
157
    {
158 35
        return $this->_store->readComments($this->getId());
159
    }
160
161
    /**
162
     * Generate the "delete" token.
163
     *
164
     * The token is the hmac of the pastes ID signed with the server salt.
165
     * The paste can be deleted by calling:
166
     * http://example.com/privatebin/?pasteid=<pasteid>&deletetoken=<deletetoken>
167
     *
168
     * @access public
169
     * @return string
170
     */
171 32
    public function getDeleteToken()
172
    {
173 32
        if (!property_exists($this->_data->meta, 'salt')) {
174
            $this->get();
175
        }
176 32
        return hash_hmac(
177 32
            $this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256',
178 32
            $this->getId(),
179 32
            $this->_data->meta->salt
180
        );
181
    }
182
183
    /**
184
     * Set paste's attachment.
185
     *
186
     * @access public
187
     * @param string $attachment
188
     * @throws Exception
189
     */
190 2 View Code Duplication
    public function setAttachment($attachment)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    {
192 2
        if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachment)) {
193
            throw new Exception('Invalid attachment.', 71);
194
        }
195 2
        $this->_data->meta->attachment = $attachment;
196 2
    }
197
198
    /**
199
     * Set paste's attachment name.
200
     *
201
     * @access public
202
     * @param string $attachmentname
203
     * @throws Exception
204
     */
205 2 View Code Duplication
    public function setAttachmentName($attachmentname)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
    {
207 2
        if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachmentname)) {
208
            throw new Exception('Invalid attachment.', 72);
209
        }
210 2
        $this->_data->meta->attachmentname = $attachmentname;
211 2
    }
212
213
    /**
214
     * Set paste expiration.
215
     *
216
     * @access public
217
     * @param string $expiration
218
     */
219 7
    public function setExpiration($expiration)
220
    {
221 7
        $expire_options = $this->_conf->getSection('expire_options');
222 7
        if (array_key_exists($expiration, $expire_options)) {
223 5
            $expire = $expire_options[$expiration];
224
        } else {
225
            // using getKey() to ensure a default value is present
226 2
            $expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
227
        }
228 7
        if ($expire > 0) {
229 7
            $this->_data->meta->expire_date = time() + $expire;
230
        }
231 7
    }
232
233
    /**
234
     * Set paste's burn-after-reading type.
235
     *
236
     * @access public
237
     * @param string $burnafterreading
238
     * @throws Exception
239
     */
240 3
    public function setBurnafterreading($burnafterreading = '1')
241
    {
242 3
        if ($burnafterreading === '0') {
243 1
            $this->_data->meta->burnafterreading = false;
244
        } else {
245 3
            if ($burnafterreading !== '1') {
246 2
                throw new Exception('Invalid data.', 73);
247
            }
248 1
            $this->_data->meta->burnafterreading = true;
249 1
            $this->_data->meta->opendiscussion   = false;
250
        }
251 1
    }
252
253
    /**
254
     * Set paste's discussion state.
255
     *
256
     * @access public
257
     * @param string $opendiscussion
258
     * @throws Exception
259
     */
260 11
    public function setOpendiscussion($opendiscussion = '1')
261
    {
262
        if (
263 11
            !$this->_conf->getKey('discussion') ||
264 11
            $this->isBurnafterreading() ||
265 11
            $opendiscussion === '0'
266
        ) {
267 1
            $this->_data->meta->opendiscussion = false;
268
        } else {
269 11
            if ($opendiscussion !== '1') {
270 2
                throw new Exception('Invalid data.', 74);
271
            }
272 9
            $this->_data->meta->opendiscussion = true;
273
        }
274 9
    }
275
276
    /**
277
     * Set paste's format.
278
     *
279
     * @access public
280
     * @param string $format
281
     * @throws Exception
282
     */
283 8
    public function setFormatter($format)
284
    {
285 8
        if (!array_key_exists($format, $this->_conf->getSection('formatter_options'))) {
286 2
            $format = $this->_conf->getKey('defaultformatter');
287
        }
288 8
        $this->_data->meta->formatter = $format;
289 8
    }
290
291
    /**
292
     * Check if paste is of burn-after-reading type.
293
     *
294
     * @access public
295
     * @throws Exception
296
     * @return bool
297
     */
298 25 View Code Duplication
    public function isBurnafterreading()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
    {
300 25
        if (!property_exists($this->_data, 'data')) {
301 14
            $this->get();
302
        }
303 23
        return property_exists($this->_data->meta, 'burnafterreading') &&
304 23
               $this->_data->meta->burnafterreading === true;
305
    }
306
307
    /**
308
     * Check if paste has discussions enabled.
309
     *
310
     * @access public
311
     * @throws Exception
312
     * @return bool
313
     */
314 13 View Code Duplication
    public function isOpendiscussion()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
    {
316 13
        if (!property_exists($this->_data, 'data')) {
317 8
            $this->get();
318
        }
319 13
        return property_exists($this->_data->meta, 'opendiscussion') &&
320 13
               $this->_data->meta->opendiscussion === true;
321
    }
322
}
323