AbstractRepository::getRepositoryPath()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * Copyright (C) 2017 by TEQneers GmbH & Co. KG
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be included in
13
 * all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
 * THE SOFTWARE.
22
 */
23
24
/**
25
 * Git Stream Wrapper for PHP
26
 *
27
 * @category   TQ
28
 * @package    TQ_VCS
29
 * @subpackage VCS
30
 * @copyright  Copyright (C) 2018 by TEQneers GmbH & Co. KG
31
 */
32
33
namespace TQ\Vcs\Repository;
34
use TQ\Vcs\FileSystem;
35
36
/**
37
 * Base class for VCS repositories
38
 *
39
 * @author     Stefan Gehrig <gehrigteqneers.de>
40
 * @category   TQ
41
 * @package    TQ_VCS
42
 * @subpackage VCS
43
 * @copyright  Copyright (C) 2018 by TEQneers GmbH & Co. KG
44
 */
45
abstract class AbstractRepository implements RepositoryInterface
46
{
47
    /**
48
     * The repository path
49
     *
50
     * @var string
51
     */
52
    protected $repositoryPath;
53
54
    /**
55
     * The mode used to create files when requested
56
     *
57
     * @var integer
58
     */
59
    protected $fileCreationMode  = 0644;
60
61
    /**
62
     * The mode used to create directories when requested
63
     *
64
     * @var integer
65
     */
66
    protected $directoryCreationMode = 0755;
67
68
    /**
69
     * The author used when committing changes
70
     *
71
     * @var string
72
     */
73
    protected $author;
74
75
    /**
76
     * Creates a new repository instance - use {@see open()} instead
77
     *
78
     * @param   string     $repositoryPath
79
     */
80 193
    protected function __construct($repositoryPath)
81
    {
82 193
        $this->repositoryPath   = rtrim($repositoryPath, '/');
83 193
    }
84
85
    /**
86
     * Returns the full file system path to the repository
87
     *
88
     * @return  string
89
     */
90 189
    public function getRepositoryPath()
91
    {
92 189
        return $this->repositoryPath;
93
    }
94
95
    /**
96
     * Returns the mode used to create files when requested
97
     *
98
     * @return  integer
99
     */
100 41
    public function getFileCreationMode()
101
    {
102 41
        return $this->fileCreationMode;
103
    }
104
105
    /**
106
     * Sets the mode used to create files when requested
107
     *
108
     * @param   integer     $fileCreationMode   The mode, e.g. 644
109
     * @return  RepositoryInterface
110
     */
111
    public function setFileCreationMode($fileCreationMode)
112
    {
113
        $this->fileCreationMode  = (int)$fileCreationMode;
114
        return $this;
115
    }
116
117
    /**
118
     * Returns the mode used to create directories when requested
119
     *
120
     * @return  integer
121
     */
122 41
    public function getDirectoryCreationMode()
123
    {
124 41
        return $this->directoryCreationMode;
125
    }
126
127
    /**
128
     * Sets the mode used to create directories when requested
129
     *
130
     * @param   integer     $directoryCreationMode   The mode, e.g. 755
131
     * @return  RepositoryInterface
132
     */
133
    public function setDirectoryCreationMode($directoryCreationMode)
134
    {
135
        $this->directoryCreationMode  = (int)$directoryCreationMode;
136
        return $this;
137
    }
138
139
    /**
140
     * Returns the author used when committing changes
141
     *
142
     * @return  string
143
     */
144 100
    public function getAuthor()
145
    {
146 100
        return $this->author;
147
    }
148
149
    /**
150
     * Sets the author used when committing changes
151
     *
152
     * @param   string     $author      The author used when committing changes
153
     * @return  RepositoryInterface
154
     */
155
    public function setAuthor($author)
156
    {
157
        $this->author  = (string)$author;
158
        return $this;
159
    }
160
161
    /**
162
     * Resolves an absolute path into a path relative to the repository path
163
     *
164
     * @param   string|array  $path         A file system path (or an array of paths)
165
     * @return  string|array                Either a single path or an array of paths is returned
166
     */
167 133
    public function resolveLocalPath($path)
168
    {
169 133
        if (is_array($path)) {
170 107
            $paths  = array();
171 107
            foreach ($path as $p) {
172 107
                $paths[]    = $this->resolveLocalPath($p);
173
            }
174 107
            return $paths;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $paths; (array) is incompatible with the return type declared by the interface TQ\Vcs\Repository\Reposi...rface::resolveLocalPath of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
175
        } else {
176 133
            $path   = FileSystem::normalizeDirectorySeparator($path);
177 133
            if (strpos($path, $this->getRepositoryPath()) === 0) {
178 116
                $path  = substr($path, strlen($this->getRepositoryPath()));
179
            }
180 133
            return ltrim($path, '/');
181
        }
182
    }
183
184
    /**
185
     * Resolves a path relative to the repository into an absolute path
186
     *
187
     * @param   string|array  $path     A local path (or an array of paths)
188
     * @return  string|array            Either a single path or an array of paths is returned
189
     */
190 159
    public function resolveFullPath($path)
191
    {
192 159
        if (is_array($path)) {
193 53
            $paths  = array();
194 53
            foreach ($path as $p) {
195 53
                $paths[]    = $this->resolveFullPath($p);
196
            }
197 53
            return $paths;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $paths; (array) is incompatible with the return type declared by the interface TQ\Vcs\Repository\Reposi...erface::resolveFullPath of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
198
        } else {
199 159
            if (strpos($path, $this->getRepositoryPath()) === 0) {
200 117
                return $path;
201
            }
202 86
            $path  = FileSystem::normalizeDirectorySeparator($path);
203 86
            $path  = ltrim($path, '/');
204 86
            return $this->getRepositoryPath().'/'.$path;
205
        }
206
    }
207
208
    /**
209
     * Runs $function in a transactional scope committing all changes to the repository on success,
210
     * but rolling back all changes in the event of an Exception being thrown in the closure
211
     *
212
     * The closure $function will be called with a {@see TQ\Vcs\Repository\Transaction} as its only argument
213
     *
214
     * @param   \Closure   $function        The callback used inside the transaction
215
     * @return  Transaction
216
     * @throws  \Exception                  Rethrows every exception happening inside the transaction
217
     */
218 8
    public function transactional(\Closure $function)
219
    {
220
        try {
221 8
            $transaction    = new Transaction($this);
222 8
            $result         = $function($transaction);
223
224 6
            $this->add(null);
225 6
            if ($this->isDirty()) {
226 4
                $commitMsg  = $transaction->getCommitMsg();
227 4
                if (empty($commitMsg)) {
228
                    $commitMsg  = sprintf(
229
                        '%s did a transactional commit in "%s"',
230
                        __CLASS__,
231
                        $this->getRepositoryPath()
232
                    );
233
                }
234 4
                $this->commit($commitMsg, null, $transaction->getAuthor());
235
            }
236 6
            $commitHash  = $this->getCurrentCommit();
237 6
            $transaction->setCommitHash($commitHash);
238 6
            $transaction->setResult($result);
239 6
            return $transaction;
240 2
        } catch (\Exception $e) {
241 2
            $this->reset();
242 2
            throw $e;
243
        }
244
    }
245
}
246
247