77
88namespace ZendService \Twitter ;
99
10+ use Closure ;
1011use Traversable ;
1112use ZendOAuth as OAuth ;
1213use Zend \Http ;
@@ -803,14 +804,21 @@ public function friendshipsCreate($id, array $params = []) : Response
803804 *
804805 * Returns the next cursor if there are more to be returned.
805806 *
806- * @param int|string $id
807+ * $id may be one of any of the following:
808+ *
809+ * - a single user identifier
810+ * - a single screen name
811+ * - a list of user identifiers
812+ * - a list of screen names
813+ *
814+ * @param int|string|array $id
807815 * @throws Http\Client\Exception\ExceptionInterface if HTTP request fails or times out
808816 * @throws Exception\DomainException if unable to decode JSON payload
809817 */
810818 public function friendshipsLookup ($ id , array $ params = []) : Response
811819 {
812820 $ path = 'friendships/lookup ' ;
813- $ params = $ this ->createUserParameter ($ id , $ params );
821+ $ params = $ this ->createUserListParameter ($ id , $ params, __METHOD__ );
814822 return $ this ->get ($ path , $ params );
815823 }
816824
@@ -1335,15 +1343,21 @@ public function statusesUserTimeline(array $options = []) : Response
13351343 *
13361344 * This is the most effecient way of gathering bulk user data.
13371345 *
1338- * @param int|string $id
1346+ * $id may be one of any of the following:
1347+ *
1348+ * - a single user identifier
1349+ * - a single screen name
1350+ * - a list of user identifiers
1351+ * - a list of screen names
1352+ *
1353+ * @param int|string|array $id
13391354 * @throws Http\Client\Exception\ExceptionInterface if HTTP request fails or times out
13401355 * @throws Exception\DomainException if unable to decode JSON payload
13411356 */
13421357 public function usersLookup ($ id , array $ params = []) : Response
13431358 {
13441359 $ path = 'users/lookup ' ;
1345- // $params = $this->createUserParameter($id, $params);
1346- $ params ['user_id ' ] = $ id ;
1360+ $ params = $ this ->createUserListParameter ($ id , $ params , __METHOD__ );
13471361 return $ this ->post ($ path , $ params );
13481362 }
13491363
@@ -1446,16 +1460,17 @@ public function init(string $path, Http\Client $client) : void
14461460 * returned verbatim. Otherwise, a zero is returned.
14471461 *
14481462 * @param string|int $int
1449- * @return string|int
14501463 */
1451- protected function validInteger ($ int )
1464+ protected function validInteger ($ int ) : int
14521465 {
1453- if (is_int ($ int )) {
1466+ if (is_int ($ int ) && $ int > - 1 ) {
14541467 return $ int ;
14551468 }
1469+
14561470 if (is_string ($ int ) && preg_match ('/^(\d+)$/ ' , $ int )) {
14571471 return $ int ;
14581472 }
1473+
14591474 return 0 ;
14601475 }
14611476
@@ -1467,12 +1482,11 @@ protected function validInteger($int)
14671482 protected function validateScreenName (string $ name ) : string
14681483 {
14691484 if (! is_string ($ name ) || ! preg_match ('/^[a-zA-Z0-9_]{1,15}$/ ' , $ name )) {
1470- throw new Exception \InvalidArgumentException (
1471- 'Screen name, " '
1472- . $ name
1473- . '" should only contain alphanumeric characters and '
1474- . ' underscores, and not exceed 15 characters. '
1475- );
1485+ throw new Exception \InvalidArgumentException (sprintf (
1486+ 'Screen name, "%s" should only contain alphanumeric characters and '
1487+ . ' underscores, and not exceed 15 characters. ' ,
1488+ $ name
1489+ ));
14761490 }
14771491 return $ name ;
14781492 }
@@ -1513,15 +1527,106 @@ protected function performPost(string $method, $data, Http\Client $client) : Htt
15131527 *
15141528 * @param int|string $id
15151529 * @param array $params
1530+ * @throws Exception\InvalidArgumentException if the value is neither an integer nor a string
15161531 */
15171532 protected function createUserParameter ($ id , array $ params ) : array
15181533 {
1519- if ($ this ->validInteger ($ id )) {
1534+ if (! is_string ($ id ) && ! is_int ($ id )) {
1535+ throw new Exception \InvalidArgumentException (sprintf (
1536+ '$id must be an integer or a string, received %s ' ,
1537+ is_object ($ id ) ? get_class ($ id ) : gettype ($ id )
1538+ ));
1539+ }
1540+
1541+ if (0 !== $ this ->validInteger ($ id )) {
15201542 $ params ['user_id ' ] = $ id ;
15211543 return $ params ;
15221544 }
15231545
1546+ if (! is_string ($ id )) {
1547+ throw new Exception \InvalidArgumentException (sprintf (
1548+ '$id must be an integer or a string, received %s ' ,
1549+ gettype ($ id )
1550+ ));
1551+ }
1552+
15241553 $ params ['screen_name ' ] = $ this ->validateScreenName ($ id );
15251554 return $ params ;
15261555 }
1556+
1557+ /**
1558+ * Prepares a list of identifiers for use with endpoints accepting lists of users.
1559+ *
1560+ * The various `lookup` endpoints allow passing either:
1561+ *
1562+ * - a single user identifier
1563+ * - a single screen name
1564+ * - a list of user identifiers
1565+ * - a list of screen names
1566+ *
1567+ * This method checks for each of these conditions. For scalar $ids, it
1568+ * proxies to {@link createUserParameter}. Otherwise, it checks to ensure
1569+ * that all values are of the same type. For identifiers, it then
1570+ * concatenates the values and returns them in the `user_id` parameter.
1571+ * For screen names, it validates them first, before concatenating and
1572+ * returning them via the `screen_name` parameter.
1573+ *
1574+ * @param int|string|array $ids
1575+ * @throws Exception\InvalidArgumentException for a non-int/string/array $ids value.
1576+ * @throws Exception\InvalidArgumentException if an array of $ids exceeds 100 items.
1577+ * @throws Exception\InvalidArgumentException if an array of $ids are not all of the same type.
1578+ * @throws Exception\InvalidArgumentException if any screen name element is invalid.
1579+ */
1580+ protected function createUserListParameter ($ ids , array $ params , string $ context ) : array
1581+ {
1582+ if (! is_array ($ ids )) {
1583+ return $ this ->createUserParameter ($ ids , $ params );
1584+ }
1585+
1586+ if (100 < count ($ ids )) {
1587+ throw new Exception \InvalidArgumentException (sprintf (
1588+ 'Lists of identifier(s) or screen name(s) provided for %s; '
1589+ . 'must contain no more than 100 items. '
1590+ . 'Received %d ' ,
1591+ $ context ,
1592+ count ($ ids )
1593+ ));
1594+ }
1595+
1596+ $ detectedType = array_reduce ($ ids , function ($ detectedType , $ id ) {
1597+ if (false === $ detectedType ) {
1598+ return $ detectedType ;
1599+ }
1600+
1601+ $ idType = 0 !== $ this ->validInteger ($ id )
1602+ ? 'user_id '
1603+ : 'screen_name ' ;
1604+
1605+ if (null === $ detectedType ) {
1606+ return $ idType ;
1607+ }
1608+
1609+ return $ detectedType === $ idType
1610+ ? $ detectedType
1611+ : false ;
1612+ }, null );
1613+
1614+ if (false === $ detectedType ) {
1615+ throw new Exception \InvalidArgumentException (sprintf (
1616+ 'Invalid identifier(s) or screen name(s) provided for %s; '
1617+ . 'all values must either be identifiers OR screen names. '
1618+ . 'You cannot provide items of both types. ' ,
1619+ $ context
1620+ ));
1621+ }
1622+
1623+ if ($ detectedType === 'user_id ' ) {
1624+ $ params ['user_id ' ] = implode (', ' , $ ids );
1625+ return $ params ;
1626+ }
1627+
1628+ array_walk ($ ids , Closure::fromCallable ([$ this , 'validateScreenName ' ]));
1629+ $ params ['screen_name ' ] = implode (', ' , $ ids );
1630+ return $ params ;
1631+ }
15271632}
0 commit comments