Completed
Push — master ( 7fe9d8...8ae4a4 )
by Dominik
08:37
created

createUserContactFromLinkedInProfile()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 14
cp 0
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 18
nc 8
nop 1
crap 20
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 array of provider ids that are loaded already
32
	 */
33
	private $loadedProviders;
34
	
35
	/**
36
	 * @var ContactSorter
37
	 */
38
	private $sorter;
39
	
40
	/**
41
	 * @var ContactMerger
42
	 */
43
	private $merger;
44
45
	/**
46
	 * @var GenderGuesser
47
	 */
48
	private $genderGuesser;
49
50
	/**
51
	 * @var string
52
	 */
53
	const CONTACTS_SESSION_NAME = "hybrid_auth_contacts";
54
	const LOADED_PROVIDERS_NAME = "hybrid_auth_loaded_providers";
55
56
	/**
57
	 * Get the contacts from all configured providers
58
	 * @param AzineHybridAuth $hybridAuth
59
	 * @param Session $session
60
	 * @param array $providers
61
	 */
62 1
	public function __construct(AzineHybridAuth $hybridAuth, Session $session, ContactSorter $sorter, ContactMerger $merger, GenderGuesser $genderGuesser, ContactFilter $contactFilter, array $providers){
63 1
		$this->hybridAuth = $hybridAuth;
64 1
		$this->sorter = $sorter;
65 1
		$this->merger = $merger;
66 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...
67 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...
68 1
		$this->providers = array_keys($providers);
69 1
		$this->session = $session;
70 1
		$this->genderGuesser = $genderGuesser;
71 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...
72 1
	}
73
74
    /**
75
     * Get user-profiles from xing and linked-in
76
     * @param int $pageSize
77
     * @param int $offset
78
     * @param array $filterParams
79
     * @return array
80
     */
81 1
	public function getContactProfiles($pageSize = 50, $offset = 0, $filterParams = array()){
82
83
		// check if the contacts are loaded already
84 1
		if(sizeof($this->providers) != sizeof($this->loadedProviders)){
85
			$this->getAllContacts();
86
		}
87
88
		// filter according to the $filterParams
89 1
		$contacts = $this->contactFilter->filter($this->contacts, $filterParams);
90
91
		// return one page
92 1
		$contacts =  array_slice($contacts, $offset, $pageSize, true);
93 1
		return $contacts;
94
	}
95
96
	/**
97
	 * Fetch all contacts from the networks
98
	 */
99
	private function getAllContacts(){
100
		$newContactsCount = 0;
101
		foreach ($this->providers as $provider){
102
			$connected = $this->hybridAuth->getProvider(null, $provider, false)->isUserConnected();
103
			if($connected && (!array_key_exists($provider, $this->loadedProviders) || sizeof($this->loadedProviders[$provider]) == 0)){
104
				$newContacts = $this->getUserContactsFor($provider);
105
				$this->loadedProviders[$provider] = $newContacts;
106
				$this->session->set(self::LOADED_PROVIDERS_NAME, $this->loadedProviders);
107
				$this->session->save();
108
				$newContactsCount += sizeof($newContacts);
109
			}
110
		}
111
112
		if($newContactsCount > 0) {
113
			// merge the old and new contacts
114
			$this->contacts = $this->merger->merge($this->loadedProviders);
115
116
			// sort all contacts
117
			usort($this->contacts, array($this->sorter, 'compare'));
118
119
			$this->session->set(self::CONTACTS_SESSION_NAME, $this->contacts);
120
			$this->session->save();
121
		}
122
	}
123
124
	/**
125
	 * Get ALL xing contacts of the current user
126
	 * @param $provider
127
	 * @return array
128
	 * @throws \Exception
129
	 */
130
	public function getUserContactsFor($provider){
131
		if($provider == "Xing"){
132
			return $this->getXingContacts();
133
		} elseif ($provider == "LinkedIn"){
134
			return $this->getLinkedInContacts();
135
		}
136
137
		$userContacts = array();
138
		foreach ($this->hybridAuth->getProvider(null, $provider)->getUserContacts() as $next){
139
			$nextContact = new UserContact($provider);
140
			$nextContact->identifier	= $next->identifier;
141
			$nextContact->profileURL	= $next->profileURL;
142
			$nextContact->firstName 	= $next->firstName;
143
			$nextContact->lastName		= $next->lastName;
144
			$nextContact->displayName	= $nextContact->firstName." ".$nextContact->lastName;
145
			$nextContact->description	= $next->description;
146
			$nextContact->email			= $next->email;
147
		}
148
		return $userContacts;
149
	}
150
151
    /**
152
     * Get ALL xing contacts of the current user
153
     * @throws \Exception
154
     * @return array of UserContact
155
     */
156
	public function getXingContacts(){
157
        /** @var \OAuth1Client $api */
158
		$api = $this->hybridAuth->getXingApi();
159
		$fetchSize = 100;
160
		$fetchOffset = 0;
161
		$fetchMore = true;
162
		$users = array();
163
		try {
164
			while ($fetchMore){
165
				$oResponse = $api->get("users/me/contacts?limit=$fetchSize&user_fields=id,display_name,permalink,web_profiles,photo_urls,first_name,last_name,interests,gender,active_email,professional_experience&offset=$fetchOffset");
166
				if(isset($oResponse->error_name)){
167
					throw new \Exception($oResponse->error_name." : ".$oResponse->message);
168
				}
169
				$users = array_merge($users, $oResponse->contacts->users);
170
				$fetchOffset = $fetchOffset + $fetchSize;
171
				$fetchMore = $fetchSize == sizeof($oResponse->contacts->users);
172
			}
173
		}
174
		catch(\Exception $e) {
175
			throw new \Exception('Could not fetch contacts. Xing returned an error.', $e->getCode(), $e);
176
		}
177
178
179
		// Create the contacts array.
180
		$xingContacts = array();
181
		foreach($users as $connection) {
182
			$xingContacts[] = $this->createUserContactFromXingProfile($connection);
183
		}
184
185
		return $xingContacts;
186
	}
187
188
    /**
189
     * Get ALL linkedin contacts of the current user
190
     * @throws \Exception
191
     * @return array of UserContact
192
     */
193
	public function getLinkedInContacts(){
194
	    /** @var \OAuth2Client $api */
195
		$api = $this->hybridAuth->getLinkedInApi();
196
		$fetchSize = 500;
197
		$fetchMore = true;
198
		$fetchOffset = 0;
199
		$users = array();
200
201
		try{
202
			while ($fetchMore){
203
				$response = $api->get("~/connections:(id,first-name,last-name,picture-url,public-profile-url,summary,headline,specialities)?start=$fetchOffset&count=$fetchSize");
204
				if($response && array_key_exists('linkedin', $response)) {
205
                    $connectionsXml = new \SimpleXMLElement($response['linkedin']);
206
                    foreach ($connectionsXml->person as $person) {
207
                        $users[] = $person;
208
                    }
209
                    $fetchMore = $fetchSize == sizeof($connectionsXml->person);
210
                    $fetchOffset = $fetchOffset + $fetchSize;
211
                } else {
212
				    $fetchMore = false;
213
                }
214
			}
215
		}
216
		catch( \LinkedInException $e ){
0 ignored issues
show
Bug introduced by
The class LinkedInException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
217
			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...
218
		}
219
220
		$contacts = array();
221
		foreach( $users as $connection ) {
222
            $contacts[] = $this->createUserContactFromLinkedInProfile($connection);
223
		}
224
225
		return $contacts;
226
	}
227
	
228
	/**
229
	 * Get the basic profile of the current users contact with the given user id.
230
	 * @param string $provider
231
	 * @param string $contactId
232
	 * @return UserContact
233
	 */
234
	public function getUserContactBasicProfile($provider, $contactId){
235
		if(!array_key_exists($provider, $this->loadedProviders)){
236
			$this->loadedProviders[$provider] = $this->getUserContactsFor($provider);
237
			$this->session->set(self::LOADED_PROVIDERS_NAME, $this->loadedProviders);
238
			$this->session->save();
239
		}
240
		
241
		foreach ($this->loadedProviders[$provider] as $userContact){
242
			if($userContact->identifier == $contactId){
243
				return $userContact;
244
			}
245
		}
246
		return null;
247
	}
248
249
    /**
250
     * Get the basic profile of the user with the given profileUrl
251
     * @param string $profileUrl
252
     * @throws \Exception
253
     * @return UserContact
254
     */
255
    public function getUserProfileByUrl($profileUrl){
256
        $matches = array();
257
        preg_match('/https?:\/\/.{0,5}(xing|linkedin)(\.ch|\.com).*/', $profileUrl, $matches);
258
        $provider = $matches[1];
259
        if(strpos($provider, "xing") !== false ){
260
            $matches = array();
261
            preg_match('/.*\/profile\/([]a-z,A-Z,_,0-9]*).*/', $profileUrl, $matches);
262
            if(sizeof($matches) != 2){
263
                return null;
264
            }
265
            $profilePage = $matches[1];
266
            $xingProfiles = $this->hybridAuth->getXingApi()->get("users/$profilePage.json");
267
            return $this->createUserContactFromXingProfile($xingProfiles->users[0]);
268
269
        } elseif (strpos($provider, "linkedin") !== false){
270
            $profileUrl = urlencode($profileUrl);
271
            try{
272
                $response = $this->hybridAuth->getLinkedInApi()->connections("url=$profileUrl:(id,first-name,last-name,picture-url,public-profile-url,summary,headline,specialities,email-address)");
273
            } catch( \LinkedInException $e ){
0 ignored issues
show
Bug introduced by
The class LinkedInException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
274
                throw new \Exception( "User profile by url request failed! linkedin returned an error.", $e->getCode(), $e );
275
            }
276
            $connectionsXml = new \SimpleXMLElement( $response['linkedin'] );
277
            return $this->createUserContactFromLinkedInProfile($connectionsXml);
278
        }
279
    }
280
281
	/**
282
	 * @param $xingProfile
283
	 * @return UserContact
284
	 */
285
    private function createUserContactFromXingProfile($xingProfile){
286
        $newContact = new UserContact("Xing");
287
        $newContact->identifier	    = (property_exists($xingProfile, 'id'))          	? $xingProfile->id                  : '';
288
        $newContact->firstName 	    = (property_exists($xingProfile, 'first_name'))		? $xingProfile->first_name 	        : '';
289
        $newContact->lastName		= (property_exists($xingProfile, 'last_name')) 		? $xingProfile->last_name 	        : '';
290
        $newContact->displayName	= $newContact->firstName." ".$newContact->lastName;
291
        $newContact->profileURL	    = (property_exists($xingProfile, 'permalink'))   	? $xingProfile->permalink           : '';
292
        $newContact->photoURL       = (property_exists($xingProfile, 'photo_urls'))   	? $xingProfile->photo_urls->size_96x96   : '';
293
        $newContact->photoUrlBig    = (property_exists($xingProfile, 'photo_urls'))   	? $xingProfile->photo_urls->size_256x256   : '';
294
        $newContact->description	= (property_exists($xingProfile, 'interests'))   	? $xingProfile->interests           : '';
295
        $newContact->description	.= (property_exists($xingProfile, 'haves'))   	    ? "\n".$xingProfile->haves           : '';
296
        $newContact->description	.= (property_exists($xingProfile, 'wants'))   	    ? "\n".$xingProfile->wants           : '';
297
        $newContact->email			= (property_exists($xingProfile, 'active_email'))	? $xingProfile->active_email        : '';
298
        $newContact->gender		    = (property_exists($xingProfile, 'gender'))			? $xingProfile->gender              : '';
299
        $primaryCompany             = (property_exists($xingProfile, 'professional_experience') && property_exists($xingProfile->professional_experience, 'primary_company')) ? $xingProfile->professional_experience->primary_company : null;
300
        // company name and title are not always available.
301
        if($primaryCompany) {
302
			$newContact->company = (property_exists($primaryCompany, 'name'))	? $primaryCompany->name        : '';
303
			$newContact->title = (property_exists($primaryCompany, 'title'))	? $primaryCompany->title        : '';
304
            if($newContact->title && $newContact->company) {
305
                $newContact->headline = $newContact->title . " @ " . $newContact->company;
306
            } else {
307
                $newContact->headline = $newContact->title . $newContact->company;
308
            }
309
        }
310
311
        // My own priority: Homepage, blog, other, something else.
312
        if (property_exists($xingProfile, 'web_profiles')) {
313
            $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'homepage')) ? $xingProfile->web_profiles->homepage[0] : null;
314 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...
315
                $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'blog')) ? $xingProfile->web_profiles->blog[0] : null;
316
            }
317 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...
318
                $newContact->webSiteURL = (property_exists($xingProfile->web_profiles, 'other')) ? $xingProfile->web_profiles->other[0] : null;
319
            }
320
            // Just use *anything*!
321
            if (null === $newContact->webSiteURL) {
322
                foreach ($xingProfile->web_profiles as $aUrl) {
323
                    $newContact->webSiteURL = $aUrl[0];
324
                    break;
325
                }
326
            }
327
        }
328
329
        return $newContact;
330
    }
331
332
	/**
333
	 * @param $linkedinProfile
334
	 * @return UserContact
335
	 */
336
    private function createUserContactFromLinkedInProfile($linkedinProfile){
337
        $newContact = new UserContact("LinkedIn");
338
        $newContact->identifier  = (string) $linkedinProfile->id;
339
        $newContact->firstName   = (string) $linkedinProfile->{'first-name'};
340
        $newContact->lastName    = (string) $linkedinProfile->{'last-name'};
341
        $newContact->displayName = (string) $linkedinProfile->{'first-name'} . " " . $linkedinProfile->{'last-name'};
342
        $newContact->profileURL  = (string) $linkedinProfile->{'public-profile-url'};
343
        if($newContact->profileURL == null) {
344
            $newContact->profileURL = (string)$linkedinProfile->{'site-standard-profile-request'};
345
        }
346
        $newContact->photoURL    = (string) $linkedinProfile->{'picture-url'};
347
        $newContact->description = (string) $linkedinProfile->{'summary'};
348
        $newContact->description .= $newContact->description == "" ? (string) $linkedinProfile->{'specialities'} : "\n". (string) $linkedinProfile->{'specialities'};;
349
		if($linkedinProfile->{'email-address'}) {
350
			$newContact->email = (string)$linkedinProfile->{'email-address'};
351
		}
352
        $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...
353
        $headline	             = (string) $linkedinProfile->{'headline'};
354
        $newContact->headline = str_replace(" at ", " @ ", $headline);
355
356
        return $newContact;
357
    }
358
}