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 classes like RemoteStorageService 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 RemoteStorageService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
43 | class RemoteStorageService extends OAuthService |
||
44 | { |
||
45 | /** @var RemoteStorage */ |
||
46 | private $remoteStorage; |
||
47 | |||
48 | /** @var ApprovalManagementStorage */ |
||
49 | private $approvalManagementStorage; |
||
50 | |||
51 | public function __construct(RemoteStorage $remoteStorage, ApprovalManagementStorage $approvalManagementStorage, TemplateManagerInterface $templateManager, ClientStorageInterface $clientStorage, ResourceServerStorageInterface $resourceServerStorage, ApprovalStorageInterface $approvalStorage, AuthorizationCodeStorageInterface $authorizationCodeStorage, AccessTokenStorageInterface $accessTokenStorage, array $options = array(), IO $io = null) |
||
52 | { |
||
53 | $this->remoteStorage = $remoteStorage; |
||
54 | $this->approvalManagementStorage = $approvalManagementStorage; |
||
55 | |||
56 | parent::__construct( |
||
57 | $templateManager, |
||
58 | $clientStorage, |
||
59 | $resourceServerStorage, |
||
60 | $approvalStorage, |
||
61 | $authorizationCodeStorage, |
||
62 | $accessTokenStorage, |
||
63 | $options, |
||
64 | $io |
||
65 | ); |
||
66 | |||
67 | $this->get( |
||
68 | '/_account', |
||
69 | function (Request $request, UserInfoInterface $userInfo) { |
||
70 | $approvalList = $this->approvalManagementStorage->getApprovalList($userInfo->getUserId()); |
||
71 | |||
72 | return $this->templateManager->render( |
||
73 | 'getAccountPage', |
||
74 | array( |
||
75 | 'approval_list' => $approvalList, |
||
76 | 'host' => $request->getHeader('Host'), |
||
77 | 'user_id' => $userInfo->getUserId(), |
||
78 | 'disk_usage' => $this->remoteStorage->getFolderSize(new Path('/'.$userInfo->getUserId().'/')), |
||
79 | 'request_url' => $request->getUrl()->toString(), |
||
80 | 'show_account_icon' => true, |
||
81 | ) |
||
82 | ); |
||
83 | }, |
||
84 | array( |
||
85 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
86 | 'activate' => array('user'), |
||
87 | ), |
||
88 | ) |
||
89 | ); |
||
90 | |||
91 | $this->delete( |
||
92 | '/_approvals', |
||
93 | function (Request $request, UserInfoInterface $userInfo) { |
||
94 | $deleteApprovalRequest = RequestValidation::validateDeleteApprovalRequest($request); |
||
95 | |||
96 | $approval = new Approval( |
||
97 | $userInfo->getUserId(), |
||
98 | $deleteApprovalRequest['client_id'], |
||
99 | $deleteApprovalRequest['response_type'], |
||
100 | $deleteApprovalRequest['scope'] |
||
101 | ); |
||
102 | $this->approvalManagementStorage->deleteApproval($approval); |
||
103 | |||
104 | return new RedirectResponse($request->getUrl()->getRootUrl().'_account', 302); |
||
105 | }, |
||
106 | array( |
||
107 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
108 | 'activate' => array('user'), |
||
109 | ), |
||
110 | ) |
||
111 | ); |
||
112 | |||
113 | $this->get( |
||
114 | '/.well-known/webfinger', |
||
115 | function (Request $request) { |
||
116 | $resource = $request->getUrl()->getQueryParameter('resource'); |
||
117 | if (null === $resource) { |
||
118 | throw new BadRequestException('resource parameter missing'); |
||
119 | } |
||
120 | if (0 !== strpos($resource, 'acct:')) { |
||
121 | throw new BadRequestException('unsupported resource type'); |
||
122 | } |
||
123 | $userAddress = substr($resource, 5); |
||
124 | $atPos = strpos($userAddress, '@'); |
||
125 | if (false === $atPos) { |
||
126 | throw new BadRequestException('invalid user address'); |
||
127 | } |
||
128 | $user = substr($userAddress, 0, $atPos); |
||
129 | $host = substr($userAddress, $atPos + 1); |
||
|
|||
130 | |||
131 | //if($host !== $request->getUrl()->getHost()) { |
||
132 | // throw new BadRequestException(sprintf('host of webfinger resource does not match host of request %s', $host)); |
||
133 | //} |
||
134 | |||
135 | $webFingerData = array( |
||
136 | 'links' => array( |
||
137 | array( |
||
138 | 'href' => sprintf('%s%s', $request->getUrl()->getRootUrl(), $user), |
||
139 | 'properties' => array( |
||
140 | 'http://remotestorage.io/spec/version' => 'draft-dejong-remotestorage-05', |
||
141 | 'http://remotestorage.io/spec/web-authoring' => null, |
||
142 | 'http://tools.ietf.org/html/rfc6749#section-4.2' => sprintf('%s_oauth/authorize?login_hint=%s', $request->getUrl()->getRootUrl(), $user), |
||
143 | 'http://tools.ietf.org/html/rfc6750#section-2.3' => null, |
||
144 | 'http://tools.ietf.org/html/rfc7233' => 'development' !== $this->options['server_mode'] ? 'GET' : null, |
||
145 | ), |
||
146 | 'rel' => 'http://tools.ietf.org/id/draft-dejong-remotestorage', |
||
147 | ), |
||
148 | // legacy -03 WebFinger response |
||
149 | array( |
||
150 | 'href' => sprintf('%s%s', $request->getUrl()->getRootUrl(), $user), |
||
151 | 'properties' => array( |
||
152 | 'http://remotestorage.io/spec/version' => 'draft-dejong-remotestorage-03', |
||
153 | 'http://tools.ietf.org/html/rfc2616#section-14.16' => 'development' !== $this->options['server_mode'] ? 'GET' : false, |
||
154 | 'http://tools.ietf.org/html/rfc6749#section-4.2' => sprintf('%s_oauth/authorize?login_hint=%s', $request->getUrl()->getRootUrl(), $user), |
||
155 | 'http://tools.ietf.org/html/rfc6750#section-2.3' => false, |
||
156 | ), |
||
157 | 'rel' => 'remotestorage', |
||
158 | ), |
||
159 | ), |
||
160 | ); |
||
161 | |||
162 | $response = new Response(200, 'application/jrd+json'); |
||
163 | $response->setBody(Json::encode($webFingerData)); |
||
164 | |||
165 | return $response; |
||
166 | }, |
||
167 | array( |
||
168 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
169 | 'enabled' => false, |
||
170 | ), |
||
171 | ) |
||
172 | ); |
||
173 | |||
174 | $this->get( |
||
175 | '/', |
||
176 | function (Request $request, UserInfoInterface $userInfo = null) { |
||
177 | return $this->templateManager->render( |
||
178 | 'indexPage', |
||
179 | array( |
||
180 | 'user_id' => null !== $userInfo ? $userInfo->getUserId() : null, |
||
181 | 'show_account_icon' => true, |
||
182 | ) |
||
183 | ); |
||
184 | }, |
||
185 | array( |
||
186 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
187 | 'activate' => array('user'), |
||
188 | 'require' => false, |
||
189 | ), |
||
190 | ) |
||
191 | ); |
||
192 | |||
193 | $this->addRoute( |
||
194 | ['GET', 'HEAD'], |
||
195 | '*', |
||
196 | function (Request $request, TokenInfo $tokenInfo = null) { |
||
197 | return $this->getObject($request, $tokenInfo); |
||
198 | }, |
||
199 | array( |
||
200 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
201 | 'activate' => array('api'), |
||
202 | 'require' => false, |
||
203 | ), |
||
204 | ) |
||
205 | ); |
||
206 | |||
207 | // put a document |
||
208 | $this->put( |
||
209 | '*', |
||
210 | function (Request $request, TokenInfo $tokenInfo) { |
||
211 | return $this->putDocument($request, $tokenInfo); |
||
212 | }, |
||
213 | array( |
||
214 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
215 | 'activate' => array('api'), |
||
216 | ), |
||
217 | 'fkooman\Rest\Plugin\ReferrerCheck\ReferrerCheckPlugin' => array( |
||
218 | 'enabled' => false, |
||
219 | ), |
||
220 | ) |
||
221 | ); |
||
222 | |||
223 | // delete a document |
||
224 | $this->delete( |
||
225 | '*', |
||
226 | function (Request $request, TokenInfo $tokenInfo) { |
||
227 | return $this->deleteDocument($request, $tokenInfo); |
||
228 | }, |
||
229 | array( |
||
230 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array( |
||
231 | 'activate' => array('api'), |
||
232 | ), |
||
233 | 'fkooman\Rest\Plugin\ReferrerCheck\ReferrerCheckPlugin' => array( |
||
234 | 'enabled' => false, |
||
235 | ), |
||
236 | ) |
||
237 | ); |
||
238 | |||
239 | // options request |
||
240 | $this->options( |
||
241 | '*', |
||
242 | function (Request $request) { |
||
243 | return $this->optionsRequest($request); |
||
244 | }, |
||
245 | array( |
||
246 | 'fkooman\Rest\Plugin\Authentication\AuthenticationPlugin' => array('enabled' => false), |
||
247 | ) |
||
248 | ); |
||
249 | } |
||
250 | |||
251 | public function getObject(Request $request, $tokenInfo) |
||
273 | |||
274 | public function getFolder(Path $path, Request $request, TokenInfo $tokenInfo) |
||
320 | |||
321 | public function getDocument(Path $path, Request $request, TokenInfo $tokenInfo = null) |
||
383 | |||
384 | public function putDocument(Request $request, TokenInfo $tokenInfo) |
||
427 | |||
428 | public function deleteDocument(Request $request, TokenInfo $tokenInfo) |
||
475 | |||
476 | public function optionsRequest(Request $request) |
||
480 | |||
481 | private function hasReadScope(Scope $i, $moduleName) |
||
498 | |||
499 | private function hasWriteScope(Scope $i, $moduleName) |
||
514 | |||
515 | /** |
||
516 | * ETag/If-Match/If-None-Match are always quoted, this method removes |
||
517 | * the quotes. |
||
518 | */ |
||
519 | public function stripQuotes($versionHeader) |
||
545 | |||
546 | public function run(Request $request = null) |
||
593 | } |
||
594 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
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.