Completed
Pull Request — master (#2)
by
unknown
03:07
created

AzineMergedBusinessNetworksProvider   C

Complexity

Total Complexity 63

Size/Duplication

Total Lines 355
Duplicated Lines 1.69 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 8.85%

Importance

Changes 17
Bugs 2 Features 3
Metric Value
wmc 63
c 17
b 2
f 3
lcom 1
cbo 6
dl 6
loc 355
rs 5.8893
ccs 17
cts 192
cp 0.0885

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A getContactProfiles() 0 14 2
B getAllContacts() 0 24 6
A getUserContactsFor() 0 20 4
B getXingContacts() 0 32 5
B getLinkedInContacts() 0 31 5
A getUserContactBasicProfile() 0 14 4
B getUserProfileByUrl() 0 25 5
F createUserContactFromXingProfile() 6 46 27
B createUserContactFromLinkedInProfile() 0 22 4

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 AzineMergedBusinessNetworksProvider 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 AzineMergedBusinessNetworksProvider, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Azine\HybridAuthBundle\Services;
3
4
use Azine\HybridAuthBundle\Entity\UserContact;
5
6
use Symfony\Component\HttpFoundation\Session\Session;
7
8
class AzineMergedBusinessNetworksProvider {
9
10
	/**
11
	 * @var AzineHybridAuth
12
	 */
13
	private $hybridAuth;
14
15
	/**
16
	 * @var array
17
	 */
18
	private $contacts;
19
20
	/**
21
	 * @var Session
22
	 */
23
	private $session;
24
25
	/**
26
	 * @var array of provider ids
27
	 */
28
	private $providers;
29
30
	/**
31
	 * @var config of the providers
32
	 */
33
	private $providersConfig;
34
35
	/**
36
	 * @var array of provider ids that are loaded already
37
	 */
38
	private $loadedProviders;
39
	
40
	/**
41
	 * @var ContactSorter
42
	 */
43
	private $sorter;
44
	
45
	/**
46
	 * @var ContactMerger
47
	 */
48
	private $merger;
49
50
	/**
51
	 * @var GenderGuesser
52
	 */
53
	private $genderGuesser;
54
55
	/**
56
	 * @var string
57
	 */
58
	const CONTACTS_SESSION_NAME = "hybrid_auth_contacts";
59
	const LOADED_PROVIDERS_NAME = "hybrid_auth_loaded_providers";
60
61
	/**
62
	 * Get the contacts from all configured providers
63
	 * @param AzineHybridAuth $hybridAuth
64
	 * @param Session $session
65
	 * @param array $providers
66
	 */
67 1
	public function __construct(AzineHybridAuth $hybridAuth, Session $session, ContactSorter $sorter, ContactMerger $merger, GenderGuesser $genderGuesser, ContactFilter $contactFilter, array $providers){
68 1
		$this->hybridAuth = $hybridAuth;
69 1
		$this->sorter = $sorter;
70 1
		$this->merger = $merger;
71 1
		$this->contacts = $session->get(self::CONTACTS_SESSION_NAME, array());
0 ignored issues
show
Documentation Bug introduced by
It seems like $session->get(self::CONT..._SESSION_NAME, array()) of type * is incompatible with the declared type array of property $contacts.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72 1
		$this->loadedProviders = $session->get(self::LOADED_PROVIDERS_NAME, array());
0 ignored issues
show
Documentation Bug introduced by
It seems like $session->get(self::LOAD...ROVIDERS_NAME, array()) of type * is incompatible with the declared type array of property $loadedProviders.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
73 1
		$this->providers = array_keys($providers);
74 1
		$this->providersConfig = $providers;
0 ignored issues
show
Documentation Bug introduced by
It seems like $providers of type array is incompatible with the declared type object<Azine\HybridAuthBundle\Services\config> of property $providersConfig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
75 1
		$this->session = $session;
76 1
		$this->genderGuesser = $genderGuesser;
77 1
		$this->contactFilter = $contactFilter;
0 ignored issues
show
Bug introduced by
The property contactFilter does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
78 1
	}
79
80
    /**
81
     * Get user-profiles from xing and linked-in
82
     * @param int $pageSize
83
     * @param int $offset
84
     * @param array $filterParams
85
     * @return array
86
     */
87 1
	public function getContactProfiles($pageSize = 50, $offset = 0, $filterParams = array()){
88
89
		// check if the contacts are loaded already
90 1
		if(sizeof($this->providers) != sizeof($this->loadedProviders)){
91
			$this->getAllContacts();
92
		}
93
94
		// filter according to the $filterParams
95 1
		$contacts = $this->contactFilter->filter($this->contacts, $filterParams);
96
97
		// return one page
98 1
		$contacts =  array_slice($contacts, $offset, $pageSize, true);
99 1
		return $contacts;
100
	}
101
102
	/**
103
	 * Fetch all contacts from the networks
104
	 */
105
	private function getAllContacts(){
106
		$newContactsCount = 0;
107
		foreach ($this->providers as $provider){
108
			$connected = $this->hybridAuth->getProvider(null, $provider, false)->isUserConnected();
109
			if($connected && (!array_key_exists($provider, $this->loadedProviders) || sizeof($this->loadedProviders[$provider]) == 0)){
110
				$newContacts = $this->getUserContactsFor($provider);
111
				$this->loadedProviders[$provider] = $newContacts;
112
				$this->session->set(self::LOADED_PROVIDERS_NAME, $this->loadedProviders);
113
				$this->session->save();
114
				$newContactsCount += sizeof($newContacts);
115
			}
116
		}
117
118
		if($newContactsCount > 0) {
119
			// merge the old and new contacts
120
			$this->contacts = $this->merger->merge($this->loadedProviders);
121
122
			// sort all contacts
123
			usort($this->contacts, array($this->sorter, 'compare'));
124
125
			$this->session->set(self::CONTACTS_SESSION_NAME, $this->contacts);
126
			$this->session->save();
127
		}
128
	}
129
130
	/**
131
	 * Get ALL xing contacts of the current user
132
	 * @param $provider
133
	 * @return array
134
	 * @throws \Exception
135
	 */
136
	public function getUserContactsFor($provider){
137
		if($provider == "Xing"){
138
			return $this->getXingContacts();
139
		} elseif ($provider == "LinkedIn"){
140
			return $this->getLinkedInContacts();
141
		}
142
143
		$userContacts = array();
144
		foreach ($this->hybridAuth->getProvider(null, $provider)->getUserContacts() as $next){
145
			$nextContact = new UserContact($provider);
146
			$nextContact->identifier	= $next->identifier;
147
			$nextContact->profileURL	= $next->profileURL;
148
			$nextContact->firstName 	= $next->firstName;
149
			$nextContact->lastName		= $next->lastName;
150
			$nextContact->displayName	= $nextContact->firstName." ".$nextContact->lastName;
151
			$nextContact->description	= $next->description;
152
			$nextContact->email			= $next->email;
153
		}
154
		return $userContacts;
155
	}
156
157
    /**
158
     * Get ALL xing contacts of the current user
159
     * @throws \Exception
160
     * @return array of UserContact
161
     */
162
	public function getXingContacts(){
163
		$api = $this->hybridAuth->getXingApi();
164
		$userFields = implode(',', $this->providersConfig['xing']['fields']);
165
		$fetchSize = 100;
166
		$fetchOffset = 0;
167
		$fetchMore = true;
168
		$users = array();
169
		try {
170
			while ($fetchMore){
171
				$uri = sprintf('users/me/contacts?limit=%1$s&user_fields=%2$s&offset=%3$s', $fetchSize, $userFields, $fetchOffset);
172
				$oResponse = $api->get($uri);
173
				if(isset($oResponse->error_name)){
174
					throw new \Exception($oResponse->error_name." : ".$oResponse->message);
175
				}
176
				$users = array_merge($users, $oResponse->contacts->users);
177
				$fetchOffset = $fetchOffset + $fetchSize;
178
				$fetchMore = $fetchSize == sizeof($oResponse->contacts->users);
179
			}
180
		}
181
		catch(\Exception $e) {
182
			throw new \Exception('Could not fetch contacts. Xing returned an error.', $e->getCode(), $e);
183
		}
184
185
186
		// Create the contacts array.
187
		$xingContacts = array();
188
		foreach($users as $connection) {
189
			$xingContacts[] = $this->createUserContactFromXingProfile($connection);
190
		}
191
192
		return $xingContacts;
193
	}
194
195
    /**
196
     * Get ALL linkedin contacts of the current user
197
     * @throws \Exception
198
     * @return array of UserContact
199
     */
200
	public function getLinkedInContacts(){
201
		$api = $this->hybridAuth->getLinkedInApi();
202
		$userFields = implode(',', $this->providersConfig['linkedin']['fields']);
203
		$fetchSize = 500;
204
		$fetchMore = true;
205
		$fetchOffset = 0;
206
		$users = array();
207
208
		try{
209
			while ($fetchMore){
210
				$uri = sprintf('~/connections:(%1$s)?start=%2$s&count=%3$s', $userFields, $fetchOffset, $fetchSize);
211
				$response = $api->profile($uri);
212
				$connectionsXml = new \SimpleXMLElement( $response['linkedin'] );
213
				foreach ($connectionsXml->person as $person){
214
					$users[] = $person;
215
				}
216
				$fetchMore = $fetchSize == sizeof($connectionsXml->person);
217
				$fetchOffset = $fetchOffset + $fetchSize;
218
			}
219
		}
220
		catch( \LinkedInException $e ){
221
			throw new \Exception( "User contacts request failed! {$this->providerId} returned an error.", $e->getCode(), $e );
0 ignored issues
show
Bug introduced by
The property providerId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
222
		}
223
224
		$contacts = array();
225
		foreach( $users as $connection ) {
226
            $contacts[] = $this->createUserContactFromLinkedInProfile($connection);
227
		}
228
229
		return $contacts;
230
	}
231
	
232
	/**
233
	 * Get the basic profile of the current users contact with the given user id.
234
	 * @param string $provider
235
	 * @param string $contactId
236
	 * @return UserContact
237
	 */
238
	public function getUserContactBasicProfile($provider, $contactId){
239
		if(!array_key_exists($provider, $this->loadedProviders)){
240
			$this->loadedProviders[$provider] = $this->getUserContactsFor($provider);
241
			$this->session->set(self::LOADED_PROVIDERS_NAME, $this->loadedProviders);
242
			$this->session->save();
243
		}
244
		
245
		foreach ($this->loadedProviders[$provider] as $userContact){
246
			if($userContact->identifier == $contactId){
247
				return $userContact;
248
			}
249
		}
250
		return null;
251
	}
252
253
    /**
254
     * Get the basic profile of the user with the given profileUrl
255
     * @param string $profileUrl
256
     * @throws \Exception
257
     * @return UserContact
258
     */
259
    public function getUserProfileByUrl($profileUrl){
260
        $matches = array();
261
        preg_match('/https?:\/\/.{0,5}(xing|linkedin)(\.ch|\.com).*/', $profileUrl, $matches);
262
        $provider = $matches[1];
263
        if(strpos($provider, "xing") !== false ){
264
            $matches = array();
265
            preg_match('/.*\/profile\/([]a-z,A-Z,_,0-9]*).*/', $profileUrl, $matches);
266
            if(sizeof($matches) != 2){
267
                return null;
268
            }
269
            $profilePage = $matches[1];
270
            $xingProfiles = $this->hybridAuth->getXingApi()->get("users/$profilePage.json");
271
            return $this->createUserContactFromXingProfile($xingProfiles->users[0]);
272
273
        } elseif (strpos($provider, "linkedin") !== false){
274
            $profileUrl = urlencode($profileUrl);
275
            try{
276
                $response = $this->hybridAuth->getLinkedInApi()->connections("url=$profileUrl:(id,first-name,last-name,picture-url,public-profile-url,summary,headline,specialities,email-address)");
277
            } catch( \LinkedInException $e ){
278
                throw new \Exception( "User profile by url request failed! linkedin returned an error.", $e->getCode(), $e );
279
            }
280
            $connectionsXml = new \SimpleXMLElement( $response['linkedin'] );
281
            return $this->createUserContactFromLinkedInProfile($connectionsXml);
282
        }
283
    }
284
285
	/**
286
	 * @param $xingProfile
287
	 * @return UserContact
288
	 */
289
    private function createUserContactFromXingProfile($xingProfile){
290
        $newContact = new UserContact("Xing");
291
        $newContact->identifier	    = (property_exists($xingProfile, 'id'))          	? $xingProfile->id                  : '';
292
        $newContact->firstName 	    = (property_exists($xingProfile, 'first_name'))		? $xingProfile->first_name 	        : '';
293
        $newContact->lastName		= (property_exists($xingProfile, 'last_name')) 		? $xingProfile->last_name 	        : '';
294
        $newContact->displayName	= $newContact->firstName." ".$newContact->lastName;
295
        $newContact->profileURL	    = (property_exists($xingProfile, 'permalink'))   	? $xingProfile->permalink           : '';
296
        $newContact->photoURL       = (property_exists($xingProfile, 'photo_urls'))   	? $xingProfile->photo_urls->size_96x96   : '';
297
        $newContact->photoUrlBig    = (property_exists($xingProfile, 'photo_urls'))   	? $xingProfile->photo_urls->size_256x256   : '';
298
        $newContact->description	= (property_exists($xingProfile, 'interests'))   	? $xingProfile->interests           : '';
299
        $newContact->description	.= (property_exists($xingProfile, 'haves'))   	    ? "\n".$xingProfile->haves           : '';
300
        $newContact->description	.= (property_exists($xingProfile, 'wants'))   	    ? "\n".$xingProfile->wants           : '';
301
        $newContact->email			= (property_exists($xingProfile, 'active_email'))	? $xingProfile->active_email        : '';
302
        $newContact->gender		    = (property_exists($xingProfile, 'gender'))			? $xingProfile->gender              : '';
303
        $primaryCompany             = (property_exists($xingProfile, 'professional_experience') && property_exists($xingProfile->professional_experience, 'primary_company')) ? $xingProfile->professional_experience->primary_company : null;
304
        // company name and title are not always available.
305
        if($primaryCompany) {
306
			$newContact->company = (property_exists($primaryCompany, 'name'))	? $primaryCompany->name        : '';
307
			$newContact->title = (property_exists($primaryCompany, 'title'))	? $primaryCompany->title        : '';
308
            if($newContact->title && $newContact->company) {
309
                $newContact->headline = $newContact->title . " @ " . $newContact->company;
310
            } else {
311
                $newContact->headline = $newContact->title . $newContact->company;
312
            }
313
        }
314
315
        // My own priority: Homepage, blog, other, something else.
316
        if (property_exists($xingProfile, 'web_profiles')) {
317
            $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'homepage')) ? $xingProfile->web_profiles->homepage[0] : null;
318 View Code Duplication
            if (null === $newContact->webSiteURL) {
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...
319
                $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'blog')) ? $xingProfile->web_profiles->blog[0] : null;
320
            }
321 View Code Duplication
            if (null === $newContact->webSiteURL) {
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...
322
                $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'other')) ? $xingProfile->web_profiles->other[0] : null;
323
            }
324
            // Just use *anything*!
325
            if (null === $newContact->webSiteURL) {
326
                foreach ($xingProfile->web_profiles as $aUrl) {
327
                    $newContact->webSiteURL = $aUrl[0];
328
                    break;
329
                }
330
            }
331
        }
332
333
        return $newContact;
334
    }
335
336
	/**
337
	 * @param $linkedinProfile
338
	 * @return UserContact
339
	 */
340
    private function createUserContactFromLinkedInProfile($linkedinProfile){
341
        $newContact = new UserContact("LinkedIn");
342
        $newContact->identifier  = (string) $linkedinProfile->id;
343
        $newContact->firstName   = (string) $linkedinProfile->{'first-name'};
344
        $newContact->lastName    = (string) $linkedinProfile->{'last-name'};
345
        $newContact->displayName = (string) $linkedinProfile->{'first-name'} . " " . $linkedinProfile->{'last-name'};
346
        $newContact->profileURL  = (string) $linkedinProfile->{'public-profile-url'};
347
        if($newContact->profileURL == null) {
348
            $newContact->profileURL = (string)$linkedinProfile->{'site-standard-profile-request'};
349
        }
350
        $newContact->photoURL    = (string) $linkedinProfile->{'picture-url'};
351
        $newContact->description = (string) $linkedinProfile->{'summary'};
352
        $newContact->description .= $newContact->description == "" ? (string) $linkedinProfile->{'specialities'} : "\n". (string) $linkedinProfile->{'specialities'};;
353
		if($linkedinProfile->{'email-address'}) {
354
			$newContact->email = (string)$linkedinProfile->{'email-address'};
355
		}
356
        $newContact->gender 	 = $this->genderGuesser->gender($newContact->firstName, 5);
0 ignored issues
show
Documentation introduced by
$newContact->firstName 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...
357
        $headline	             = (string) $linkedinProfile->{'headline'};
358
        $newContact->headline = str_replace(" at ", " @ ", $headline);
359
360
        return $newContact;
361
    }
362
}
363