diff --git a/src/Client.php b/src/Client.php index 585f026..032042e 100755 --- a/src/Client.php +++ b/src/Client.php @@ -2,6 +2,19 @@ namespace UniFi_API; +use Exception; +use UniFi_API\Exceptions\CurlGeneralErrorException; +use UniFi_API\Exceptions\CurlExtensionNotLoadedException; +use UniFi_API\Exceptions\CurlTimeoutException; +use UniFi_API\Exceptions\EmailInvalidException; +use UniFi_API\Exceptions\InvalidBaseUrlException; +use UniFi_API\Exceptions\InvalidCurlMethodException; +use UniFi_API\Exceptions\InvalidSiteNameException; +use UniFi_API\Exceptions\JsonDecodeException; +use UniFi_API\Exceptions\LoginFailedException; +use UniFi_API\Exceptions\LoginRequiredException; +use UniFi_API\Exceptions\MethodDeprecatedException; + /** * The UniFi API client class. * @@ -13,14 +26,14 @@ namespace UniFi_API; * * @package UniFi_Controller_API_Client_Class * @author Art of WiFi - * @license This class is subject to the MIT license that is bundled with this package in the file LICENSE.md + * @license This class is subject to the MIT license bundled with this package in the file LICENSE.md * @example This directory in the package repository contains a collection of examples: * https://github.com/Art-of-WiFi/UniFi-API-client/tree/master/examples */ class Client { /** Constants. */ - const CLASS_VERSION = '1.1.102'; + const CLASS_VERSION = '2.0.0'; const CURL_METHODS_ALLOWED = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']; const DEFAULT_CURL_METHOD = 'GET'; @@ -71,17 +84,17 @@ class Client * * @param string $user username to use when connecting to the UniFi controller * @param string $password password to use when connecting to the UniFi controller - * @param string $baseurl optional, base URL of the UniFi controller which *must* include an 'https://' prefix, + * @param string $baseurl base URL of the UniFi controller which *must* include an 'https://' prefix, * a port suffix (e.g. :8443) is required for non-UniFi OS controllers, * do not add trailing slashes, defaults to 'https://127.0.0.1:8443' - * @param string|null $site optional, short site name to access, defaults to 'default' - * @param string|null $version optional, the version number of the controller, defaults to '8.0.28' - * @param bool $ssl_verify optional, whether to validate the controller's SSL certificate or not, a value of true + * @param string|null $site short site name to access, defaults to 'default' + * @param string|null $version the version number of the controller, defaults to '8.0.28' + * @param bool $ssl_verify whether to validate the controller's SSL certificate or not, a value of true * is recommended for production environments to prevent potential MitM attacks, defaults * to false which disables validation of the controller's SSL certificate - * @param string $unificookie_name optional, name of the cookie to use, defaults to 'unificookie'. - * This is only necessary when you have multiple apps using this API client on the - * same web server. + * @param string $unificookie_name name of the cookie to use, defaults to 'unificookie'. This is only necessary + * when you have multiple apps using this API client on the same web server. + * @throws CurlExtensionNotLoadedException|InvalidBaseUrlException|InvalidSiteNameException */ public function __construct( string $user, @@ -94,7 +107,7 @@ class Client ) { if (!extension_loaded('curl')) { - trigger_error('The PHP curl extension is not loaded. Please correct this before proceeding!'); + throw new CurlExtensionNotLoadedException(); } $this->check_base_url($baseurl); @@ -118,6 +131,7 @@ class Client * * @see https://www.php.net/manual/en/language.oop5.decon.php * @note to force the class instance to log out when you're done, call logout() + * @throws CurlGeneralErrorException|CurlTimeoutException */ public function __destruct() { @@ -136,10 +150,10 @@ class Client * Login to the UniFi controller. * * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses - * @return bool|int returns true upon success, false or the HTTP response code (typically 400, 401, or 403) upon - * error + * @return bool returns true upon a successful login + * @throws CurlGeneralErrorException|CurlTimeoutException|LoginFailedException|CurlTimeoutException */ - public function login() + public function login(): bool { /** Skip the login process if already logged in. */ if ($this->update_unificookie()) { @@ -162,10 +176,15 @@ class Client /** Execute the cURL request and get the HTTP response code. */ curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); + $error_number = curl_errno($ch); - if (curl_errno($ch)) { - trigger_error('cURL error: ' . curl_error($ch)); + if ($error_number) { + if (in_array($error_number, [CURLE_OPERATION_TIMEDOUT, CURLE_OPERATION_TIMEOUTED])) { + throw new CurlTimeoutException(curl_error($ch), $http_code, curl_getinfo($ch)); + } + + throw new CurlGeneralErrorException(curl_error($ch), $http_code, curl_getinfo($ch)); } /** Prepare the actual login. */ @@ -187,11 +206,7 @@ class Client /** Execute the cURL request and get the HTTP response code. */ $response = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - - if (curl_errno($ch)) { - trigger_error('cURL error: ' . curl_error($ch)); - } + $http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($this->debug) { print PHP_EOL . '
';
@@ -203,29 +218,30 @@ class Client
             print '
' . PHP_EOL; } - /** Based on the HTTP response code trigger an error. */ - if ($http_code >= 400) { - trigger_error("HTTP response status received: $http_code. Probably a controller login failure"); + $error_number = curl_errno($ch); - return $http_code; + if ($error_number) { + if (in_array($error_number, [CURLE_OPERATION_TIMEDOUT, CURLE_OPERATION_TIMEOUTED])) { + throw new CurlTimeoutException(curl_error($ch), $http_code, curl_getinfo($ch)); + } + + throw new CurlGeneralErrorException(curl_error($ch), $http_code, curl_getinfo($ch)); } - curl_close($ch); - - /** Check the HTTP response code. */ + /** If the HTTP response code is 200, we are logged in. */ if ($http_code >= 200) { - $this->is_logged_in = true; - - return $this->is_logged_in; + curl_close($ch); + return $this->is_logged_in = true; } - return false; + throw new LoginFailedException($http_code); } /** * Logout from the UniFi controller. * * @return bool true upon success + * @throws CurlGeneralErrorException|CurlTimeoutException|CurlTimeoutException */ public function logout(): bool { @@ -254,9 +270,14 @@ class Client /** Execute the cURL request to logout. */ curl_exec($ch); - if (curl_errno($ch)) { - trigger_error('cURL error: ' . curl_error($ch)); - return false; + $error_number = curl_errno($ch); + + if ($error_number) { + if (in_array($error_number, [CURLE_OPERATION_TIMEDOUT, CURLE_OPERATION_TIMEOUTED])) { + throw new CurlTimeoutException(curl_error($ch), curl_getinfo($ch, CURLINFO_RESPONSE_CODE), curl_getinfo($ch)); + } + + throw new CurlGeneralErrorException(curl_error($ch), curl_getinfo($ch, CURLINFO_RESPONSE_CODE), curl_getinfo($ch)); } curl_close($ch); @@ -264,6 +285,7 @@ class Client $this->is_logged_in = false; $this->cookies = ''; $this->cookies_created_at = 0; + return true; } @@ -282,6 +304,7 @@ class Client * @param string|null $ap_mac optional, AP MAC address to which client is connected, should result in faster * authorization for the client device * @return bool true upon success + * @throws Exception */ public function authorize_guest(string $mac, int $minutes, int $up = null, int $down = null, int $megabytes = null, string $ap_mac = null): bool { @@ -312,6 +335,7 @@ class Client * * @param string $mac client MAC address * @return bool true upon success + * @throws Exception */ public function unauthorize_guest(string $mac): bool { @@ -325,6 +349,7 @@ class Client * * @param string $mac client MAC address * @return bool true upon success + * @throws Exception */ public function reconnect_sta(string $mac): bool { @@ -338,6 +363,7 @@ class Client * * @param string $mac client MAC address * @return bool true upon success + * @throws Exception */ public function block_sta(string $mac): bool { @@ -351,6 +377,7 @@ class Client * * @param string $mac client MAC address * @return bool true upon success + * @throws Exception */ public function unblock_sta(string $mac): bool { @@ -366,6 +393,7 @@ class Client * slow (up to 5 minutes) on larger controllers * @param array|string $mac array of client MAC addresses (strings) or a single MAC address string * @return bool true upon success + * @throws Exception */ public function forget_sta($mac): bool { @@ -389,6 +417,7 @@ class Client * @param bool|null $is_wired optional, defines whether the new user/client-device is wired or not * @return array|bool returns an array with a single object containing details of the new user/client-device on * success, else returns false + * @throws JsonDecodeException|LoginRequiredException */ public function create_user( string $mac, @@ -428,6 +457,7 @@ class Client * @param string $user_id id of the client-device to be modified * @param string $note optional, note to be applied to the client-device * @return bool true upon success + * @throws Exception */ public function set_sta_note(string $user_id, string $note = ''): bool { @@ -443,6 +473,7 @@ class Client * @param string $name optional, name to be applied to the client device, when empty or not set, * the existing name for the client device is removed * @return bool true upon success + * @throws Exception */ public function set_sta_name(string $user_id, string $name = ''): bool { @@ -466,6 +497,7 @@ class Client * Example values: * 'airtime_avg', 'latency_avg', 'latency_min', 'latency_max' * @return array|bool returns an array of 5-minute stats objects for the current site + * @throws JsonDecodeException|LoginRequiredException */ public function stat_5minutes_site(int $start = null, int $end = null, array $attribs = null) { @@ -485,11 +517,12 @@ class Client * * @note - defaults to the past 7*24 hours * - "bytes" are no longer returned with controller version 4.9.1 and later - * @see stat_5minutes_site() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs optional, array of attributes to collect. * @return array|bool returns an array of hourly stats objects for the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_site() for details on attribs */ public function stat_hourly_site(int $start = null, int $end = null, array $attribs = null) { @@ -506,11 +539,12 @@ class Client * * @note - defaults to the past 52*7*24 hours * - "bytes" are no longer returned with controller version 4.9.1 and later - * @see stat_5minutes_site() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs optional, array of attributes to collect. * @return array|bool returns an array of daily stats objects for the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_site() for details on attribs */ public function stat_daily_site(int $start = null, int $end = null, array $attribs = null) { @@ -527,11 +561,12 @@ class Client * * @note - defaults to the past 52 weeks (52*7*24 hours) * - "bytes" are no longer returned with controller version 4.9.1 and later - * @see stat_5minutes_site() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs optional, array of attributes to collect. * @return array|bool returns an array of monthly stats objects for the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_site() for details on attribs */ public function stat_monthly_site(int $start = null, int $end = null, array $attribs = null) { @@ -579,6 +614,7 @@ class Client * 'na-rx_bytes', * 'ng-rx_bytes', * @return array|bool returns an array of 5-minute stats objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_5minutes_aps(int $start = null, int $end = null, string $mac = null, array $attribs = null) { @@ -600,13 +636,14 @@ class Client * @note - defaults to the past 7*24 hours * - make sure that the retention policy for hourly stats is set to the correct value in * the controller settings - * @see stat_5minutes_aps() for supported attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param string|null $mac optional, AP MAC address to return stats for, when empty, * stats for all APs are returned * @param array|null $attribs optional, array of attributes to collect, default: (bytes, num_sta, time). * @return array|bool returns an array of hourly stats objects + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_aps() for supported attribs */ public function stat_hourly_aps(int $start = null, int $end = null, string $mac = null, array $attribs = null) { @@ -628,13 +665,14 @@ class Client * @note - defaults to the past 7*24 hours * - make sure that the retention policy for hourly stats is set to the correct value in * the controller settings - * @see stat_5minutes_aps() for supported attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param string|null $mac optional, AP MAC address to return stats for, when empty, * stats for all APs are returned * @param array|null $attribs optional, array of attributes to collect, default: (bytes, num_sta, time). * @return array|bool returns an array of daily stats objects + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_aps() for supported attribs */ public function stat_daily_aps(int $start = null, int $end = null, string $mac = null, array $attribs = null) { @@ -656,13 +694,14 @@ class Client * @note - defaults to the past 52 weeks (52*7*24 hours) * - make sure that the retention policy for hourly stats is set to the correct value in * the controller settings - * @see stat_5minutes_aps() for supported attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param string|null $mac optional, AP MAC address to return stats for, when empty, * stats for all APs are returned * @param array|null $attribs optional, array of attributes to collect, default: (bytes, num_sta, time). * @return array|bool returns an array of monthly stats objects + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_aps() for supported attribs */ public function stat_monthly_aps(int $start = null, int $end = null, string $mac = null, array $attribs = null) { @@ -695,6 +734,7 @@ class Client * tx_packets, satisfaction, wifi_tx_attempts, 'duration' * default value is ['rx_bytes', 'tx_bytes', 'time'] * @return array|bool returns an array of 5-minute stats objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_5minutes_user(string $mac = null, int $start = null, int $end = null, array $attribs = null) { @@ -715,12 +755,13 @@ class Client * the controller settings * - make sure that "Clients Historical Data" has been enabled in the UniFi controller settings in the Maintenance * section - * @see stat_5minutes_user() for details on attribs * @param string|null $mac optional, MAC address of the user/client device to return stats for * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of hourly stats objects + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_user() for details on attribs */ public function stat_hourly_user(string $mac = null, int $start = null, int $end = null, array $attribs = null) { @@ -745,12 +786,13 @@ class Client * the controller settings * - make sure that "Clients Historical Data" has been enabled in the UniFi controller settings in the Maintenance * section - * @see stat_5minutes_user() for details on attribs * @param string|null $mac optional, MAC address of the user/client device to return stats for * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of daily stats objects + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_user() for details on attribs */ public function stat_daily_user(string $mac = null, int $start = null, int $end = null, array $attribs = null) { @@ -780,6 +822,7 @@ class Client * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of monthly stats objects + * @throws JsonDecodeException|LoginRequiredException * @see stat_5minutes_user() for details on attribs */ public function stat_monthly_user(string $mac = null, int $start = null, int $end = null, array $attribs = null) @@ -811,6 +854,7 @@ class Client * lan-tx_bytes, lan-rx_packets, lan-tx_packets, lan-rx_dropped, lan-tx_dropped * default is ['time', 'mem', 'cpu', 'loadavg_5'] * @return array|bool returns an array of 5-minute stats objects for the gateway belonging to the current site + * @throws JsonDecodeException|LoginRequiredException */ public function stat_5minutes_gateway(int $start = null, int $end = null, array $attribs = null) { @@ -827,11 +871,12 @@ class Client * * @note - defaults to the past 7*24 hours * - requires a UniFi gateway - * @see stat_5minutes_gateway() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of hourly stats objects for the gateway belonging to the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_gateway() for details on attribs */ public function stat_hourly_gateway(int $start = null, int $end = null, array $attribs = null) { @@ -848,11 +893,12 @@ class Client * * @note - defaults to the past 52 weeks (52*7*24 hours) * - requires a UniFi gateway - * @see stat_5minutes_gateway() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of hourly stats objects for the gateway belonging to the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_gateway() for details on attribs */ public function stat_daily_gateway(int $start = null, int $end = null, array $attribs = null) { @@ -869,11 +915,12 @@ class Client * * @note - defaults to the past 52 weeks (52*7*24 hours) * - requires a UniFi gateway - * @see stat_5minutes_gateway() for details on attribs * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @param array|null $attribs array containing attributes (strings) to be returned * @return array|bool returns an array of monthly stats objects for the gateway belonging to the current site + * @throws JsonDecodeException|LoginRequiredException + * @see stat_5minutes_gateway() for details on attribs */ public function stat_monthly_gateway(int $start = null, int $end = null, array $attribs = null) { @@ -893,6 +940,7 @@ class Client * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @return array|bool returns an array of speed test result objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_speedtest_results(int $start = null, int $end = null) { @@ -913,6 +961,7 @@ class Client * @param int|null $end optional, Unix timestamp in milliseconds * @param int|null $limit optional, maximum number of events to return, defaults to 10000 * @return array|bool returns an array of IPS/IDS event objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_ips_events(int $start = null, int $end = null, int $limit = null) { @@ -936,6 +985,7 @@ class Client * @param string $type optional, client type to return sessions for, can be 'all', 'guest' or 'user'; default * value is 'all' * @return array|bool returns an array of login session objects for all devices or a single device + * @throws JsonDecodeException|LoginRequiredException */ public function stat_sessions(int $start = null, int $end = null, string $mac = null, string $type = 'all') { @@ -962,6 +1012,7 @@ class Client * @param string $mac client MAC address * @param int|null $limit optional, maximum number of sessions to get (default value is 5) * @return array|bool returns an array of login session objects for all devices or a single device + * @throws JsonDecodeException|LoginRequiredException */ public function stat_sta_sessions_latest(string $mac, int $limit = null) { @@ -979,6 +1030,7 @@ class Client * @param int|null $start optional, Unix timestamp in milliseconds * @param int|null $end optional, Unix timestamp in milliseconds * @return array|bool returns an array of authorization objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_auths(int $start = null, int $end = null) { @@ -996,6 +1048,7 @@ class Client * the returned stats per client are all-time totals, irrespective of the value of $historyhours * @param int $historyhours optional, hours to go back (default is 8760 hours or 1 year) * @return array|bool returns an array of client device objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_allusers(int $historyhours = 8760) { @@ -1011,6 +1064,7 @@ class Client * @param int $within optional, time frame in hours to go back to list guests with valid access (default = 24*365 * hours) * @return array|bool returns an array of guest device objects with valid access + * @throws JsonDecodeException|LoginRequiredException */ public function list_guests(int $within = 8760) { @@ -1026,6 +1080,7 @@ class Client * made * @return array|bool returns an array of online client device objects, or in case of a single device request, returns a * single client device object, false upon error + * @throws JsonDecodeException|LoginRequiredException */ public function list_clients(string $mac = null) { @@ -1041,6 +1096,7 @@ class Client * * @param string $mac client device MAC address * @return array|bool returns an object with the client device information + * @throws JsonDecodeException|LoginRequiredException */ public function stat_client(string $mac) { @@ -1054,6 +1110,7 @@ class Client * fingerprint_source in the client device objects, the default value is 0 * @return array|bool an array of fingerprints, contain dev_ids, dev_type_ids, family_ids, os_name_ids, os_class_ids * and vendor_ids, false upon error + * @throws JsonDecodeException|LoginRequiredException */ public function list_fingerprint_devices(int $fingerprint_source = 0) { @@ -1066,6 +1123,7 @@ class Client * @param string $client_id _id value of the client device to be modified * @param string $group_id _id value of the user group to assign client device to * @return bool true upon success + * @throws Exception */ public function set_usergroup(string $client_id, string $group_id): bool { @@ -1082,6 +1140,7 @@ class Client * @param string|null $network_id optional, _id value for the network where the ip belongs to * @param string|null $fixed_ip optional, IP address, value of client device's fixed_ip field * @return array|bool returns an array containing a single object with attributes of the updated client on success + * @throws JsonDecodeException|LoginRequiredException */ public function edit_client_fixedip(string $client_id, bool $use_fixedip, string $network_id = null, string $fixed_ip = null) { @@ -1111,6 +1170,7 @@ class Client * @param string $client_id _id value for the client device * @param string $name name of the client * @return array|bool returns an array containing a single object with attributes of the updated client on success + * @throws JsonDecodeException|LoginRequiredException */ public function edit_client_name(string $client_id, string $name) { @@ -1132,6 +1192,7 @@ class Client * Fetch user groups * * @return array|bool returns an array of user group objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_usergroups() { @@ -1146,6 +1207,7 @@ class Client * @param int $group_up limit upload bandwidth in Kbps (default = -1, which sets bandwidth to unlimited) * @return array|bool containing a single object with attributes of the new usergroup ("_id", "name", * "qos_rate_max_down", "qos_rate_max_up", "site_id") on success + * @throws JsonDecodeException|LoginRequiredException */ public function create_usergroup(string $group_name, int $group_dn = -1, int $group_up = -1) { @@ -1159,7 +1221,7 @@ class Client } /** - * Modify user group (using REST) + * Modify a user group (using REST) * * @param string $group_id _id value of the user group * @param string $site_id _id value of the site @@ -1167,6 +1229,7 @@ class Client * @param int $group_dn limit download bandwidth in Kbps (default = -1, which sets bandwidth to unlimited) * @param int $group_up limit upload bandwidth in Kbps (default = -1, which sets bandwidth to unlimited) * @return array|bool returns an array containing a single object with attributes of the updated usergroup on success + * @throws JsonDecodeException|LoginRequiredException */ public function edit_usergroup(string $group_id, string $site_id, string $group_name, int $group_dn = -1, int $group_up = -1) { @@ -1184,10 +1247,11 @@ class Client } /** - * Delete user group (using REST) + * Delete a user group (using REST) * * @param string $group_id _id value of the user group to delete * @return bool returns true on success + * @throws Exception */ public function delete_usergroup(string $group_id): bool { @@ -1200,6 +1264,7 @@ class Client * Fetch AP groups * * @return array|bool containing the current AP groups on success + * @throws JsonDecodeException|LoginRequiredException */ public function list_apgroups() { @@ -1207,12 +1272,13 @@ class Client } /** - * Create AP group + * Create an AP group. * * @param string $group_name name to assign to the AP group * @param array $device_macs optional, array containing the MAC addresses (strings) of the APs to add to the new * group * @return array|bool containing a single object with attributes of the new AP group on success + * @throws JsonDecodeException|LoginRequiredException */ public function create_apgroup(string $group_name, array $device_macs = []) { @@ -1222,13 +1288,14 @@ class Client } /** - * Modify AP group + * Modify an AP group. * * @param string $group_id _id value of the AP group to modify * @param string $group_name name to assign to the AP group * @param array $device_macs array containing the members of the AP group which overwrites the existing * group_members (passing an empty array clears the AP member list) * @return array|bool containing a single object with attributes of the updated AP group on success + * @throws JsonDecodeException|LoginRequiredException */ public function edit_apgroup(string $group_id, string $group_name, array $device_macs) { @@ -1245,10 +1312,11 @@ class Client } /** - * Delete AP group + * Delete an AP group. * * @param string $group_id _id value of the AP group to delete * @return bool returns true on success + * @throws Exception */ public function delete_apgroup(string $group_id): bool { @@ -1258,10 +1326,11 @@ class Client } /** - * Fetch firewall groups (using REST) + * Fetch firewall groups (using REST). * * @param string $group_id optional, _id value of the single firewall group to list * @return array|bool containing the current firewall groups or the selected firewall group on success + * @throws JsonDecodeException|LoginRequiredException */ public function list_firewallgroups(string $group_id = '') { @@ -1269,7 +1338,7 @@ class Client } /** - * Create a firewall group (using REST) + * Create a firewall group (using REST). * * @param string $group_name name to assign to the firewall group * @param string $group_type firewall group type; valid values are address-group, ipv6-address-group, port-group @@ -1277,6 +1346,7 @@ class Client * port numbers) * (default is an empty array) * @return array|bool containing a single object with attributes of the new firewall group on success + * @throws JsonDecodeException|LoginRequiredException */ public function create_firewallgroup(string $group_name, string $group_type, array $group_members = []) { @@ -1290,7 +1360,7 @@ class Client } /** - * Modify a firewall group (using REST) + * Modify a firewall group (using REST). * * @param string $group_id _id value of the firewall group to modify * @param string $site_id site_id value of the firewall group to modify @@ -1301,6 +1371,7 @@ class Client * @param array $group_members array containing the members of the group (IPv4 addresses, IPv6 addresses or port * numbers) which overwrites the existing group_members (default is an empty array) * @return array|bool containing a single object with attributes of the updated firewall group on success + * @throws JsonDecodeException|LoginRequiredException */ public function edit_firewallgroup(string $group_id, string $site_id, string $group_name, string $group_type, array $group_members = []) { @@ -1322,10 +1393,11 @@ class Client } /** - * Delete firewall group (using REST) + * Delete a firewall group (using REST). * * @param string $group_id _id value of the firewall group to delete * @return bool returns true on success + * @throws Exception */ public function delete_firewallgroup(string $group_id): bool { @@ -1335,9 +1407,10 @@ class Client } /** - * Fetch firewall rules (using REST) + * Fetch firewall rules (using REST). * * @return array|bool containing the current firewall rules on success + * @throws JsonDecodeException|LoginRequiredException */ public function list_firewallrules() { @@ -1345,10 +1418,11 @@ class Client } /** - * Fetch static routing settings (using REST) + * Fetch static routing settings (using REST). * * @param string $route_id _id value of the static route to get settings for * @return array|bool containing the static routes and their settings + * @throws JsonDecodeException|LoginRequiredException */ public function list_routing(string $route_id = '') { @@ -1356,9 +1430,10 @@ class Client } /** - * Fetch health metrics + * Fetch health metrics. * * @return array|bool containing health metric objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_health() { @@ -1366,11 +1441,12 @@ class Client } /** - * Fetch dashboard metrics + * Fetch dashboard metrics. * * @param boolean $five_minutes when true, return stats based on 5 minute intervals, * returns hourly stats by default (supported on controller versions 5.5.* and higher) * @return array|bool containing dashboard metric objects (available since controller version 4.9.1.alpha) + * @throws JsonDecodeException|LoginRequiredException */ public function list_dashboard(bool $five_minutes = false) { @@ -1380,9 +1456,10 @@ class Client } /** - * Fetch client devices + * Fetch client devices. * * @return array|bool containing known client device objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_users() { @@ -1390,9 +1467,10 @@ class Client } /** - * List of UniFi devices with a basic subset of properties (e.g., mac, state, adopted, disabled, type, model, name) + * List of UniFi devices with a basic subset of properties (e.g., mac, state, adopted, disabled, type, model, name). * * @return array|bool an array containing known UniFi device objects, false upon error + * @throws JsonDecodeException|LoginRequiredException */ public function list_devices_basic() { @@ -1400,12 +1478,13 @@ class Client } /** - * Fetch UniFi devices + * Fetch UniFi devices. * * @param array|string $macs optional, array containing the MAC addresses (lowercase strings) of the devices * to filter by. May also be a (lowercase) string containing a single MAC address * @return array|bool an array containing known UniFi device objects, optionally filtered by the * parameter, false upon error + * @throws JsonDecodeException|LoginRequiredException */ public function list_devices($macs = []) { @@ -1415,10 +1494,11 @@ class Client } /** - * Fetch (device) tags (using REST) + * Fetch (device) tags (using REST). * * @note this endpoint was introduced with controller versions 5.5.X * @return array|bool containing known device tag objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_tags() { @@ -1426,12 +1506,13 @@ class Client } /** - * Create (device) tag (using REST) + * Create a (device) tag (using REST). * * @note this endpoint was introduced with controller versions 5.5.X * @param string $name required, the tag name to add - * @param array|null $macs optional, an array of the MAC address(es) of the device(s) to tag with the new tag + * @param array|null $macs optional, an array of the MAC address(es) for the device(s) to tag with the new tag * @return bool return true on success + * @throws Exception */ public function create_tag(string $name, array $macs = null): bool { @@ -1445,12 +1526,13 @@ class Client } /** - * Set tagged devices (using REST) + * Set tagged devices (using REST). * * @note this endpoint was introduced with controller versions 5.5.X - * @param array $macs required, an array of the MAC address(es) of the device(s) to tag + * @param array $macs required, an array of the MAC address(es) for the device(s) to tag * @param string $tag_id required, the _id value of the tag to set * @return bool return true on success + * @throws Exception */ public function set_tagged_devices(array $macs, string $tag_id): bool { @@ -1462,11 +1544,12 @@ class Client } /** - * Get (device) tag (using REST) + * Get a (device) tag (using REST). * * @note this endpoint was introduced with controller versions 5.5.X * @param string $tag_id required, the _id value of the tag to retrieve * @return array|bool containing matching tag objects + * @throws JsonDecodeException|LoginRequiredException */ public function get_tag(string $tag_id) { @@ -1476,11 +1559,12 @@ class Client } /** - * Delete (device) tag (using REST) + * Delete a (device) tag (using REST). * * @note this endpoint was introduced with controller versions 5.5.X * @param string $tag_id required, the _id value of the tag to set * @return bool return true on success + * @throws Exception */ public function delete_tag(string $tag_id): bool { @@ -1490,10 +1574,11 @@ class Client } /** - * Fetch rogue/neighboring access points + * Fetch rogue/neighboring access points. * * @param int $within optional, hours to go back to list discovered "rogue" access points (default = 24 hours) * @return array|bool containing rogue/neighboring access point objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_rogueaps(int $within = 24) { @@ -1506,6 +1591,7 @@ class Client * Fetch known rogue access points. * * @return array|bool containing known rogue access point objects + * @throws JsonDecodeException|LoginRequiredException */ public function list_known_rogueaps() { @@ -1518,6 +1604,7 @@ class Client * @note this is an experimental function, please do not use unless you know exactly what you're doing * @param int $days number of days for which the backup must be generated * @return array|bool URL from where the backup file can be downloaded once generated, false upon failure + * @throws JsonDecodeException|LoginRequiredException */ public function generate_backup(int $days = -1) { @@ -1532,6 +1619,7 @@ class Client * @note this is an experimental function, please do not use unless you know exactly what you're doing * @param string $filepath the path to the generated backup file * @return string|bool the raw content of the backup file, false upon failure + * @throws InvalidCurlMethodException|CurlGeneralErrorException|LoginFailedException */ public function download_backup(string $filepath) { @@ -1542,6 +1630,7 @@ class Client * Fetch auto backups. * * @return array|bool containing objects with backup details on success + * @throws JsonDecodeException|LoginRequiredException */ public function list_backups() { @@ -1555,6 +1644,7 @@ class Client * * @note this is an experimental function, please do not use unless you know exactly what you're doing * @return array|bool URL from where the backup/export file can be downloaded once generated, false upon failure + * @throws JsonDecodeException|LoginRequiredException */ public function generate_backup_site() { @@ -1564,10 +1654,11 @@ class Client } /** - * Fetch sites + * Fetch sites. * * @return array|bool a list of sites on this controller that the credentials used have access to, * together with some basic attributes for each site + * @throws JsonDecodeException|LoginRequiredException */ public function list_sites() { @@ -1575,10 +1666,11 @@ class Client } /** - * Fetch sites stats + * Fetch sites stats. * * @note this endpoint was introduced with controller version 5.2.9 * @return array|bool statistics for all sites hosted on this controller + * @throws JsonDecodeException|LoginRequiredException */ public function stat_sites() { @@ -1586,11 +1678,12 @@ class Client } /** - * Create a site + * Create a site. * * @param string $description the long name for the new site * @return array|bool false on failure or a single object with attributes of the new site ("_id", "desc", "name") on * success + * @throws JsonDecodeException|LoginRequiredException */ public function create_site(string $description) { @@ -1600,10 +1693,11 @@ class Client } /** - * Delete a site + * Delete a site. * * @param string $site_id _id value of the site to delete * @return bool true on success + * @throws Exception */ public function delete_site(string $site_id): bool { @@ -1613,11 +1707,12 @@ class Client } /** - * Change the current site's name + * Change the current site's name. * * @note immediately after the change, the site is available in the output of the list_sites() function * @param string $site_name the new long name for the current site * @return bool true on success + * @throws Exception */ public function set_site_name(string $site_name): bool { @@ -1627,7 +1722,7 @@ class Client } /** - * Update site country + * Update site country. * * @param string $country_id _id value of the country key * @param object|array $payload a stdClass object or associative array containing the configuration to apply to @@ -1635,6 +1730,7 @@ class Client * returned by list_settings() for the section with the "country" key. Valid * country codes can be obtained using the list_country_codes() function/method. * @return bool true on success + * @throws Exception */ public function set_site_country(string $country_id, $payload): bool { @@ -1645,7 +1741,7 @@ class Client } /** - * Update site locale + * Update site locale. * * @param string $locale_id _id value of the locale section * @param object|array $payload stdClass object or associative array containing the configuration to apply to the @@ -1655,6 +1751,7 @@ class Client * https://stackoverflow.com/questions/38399465/how-to-get-list-of-all-timezones-in-javascript * or in PHP using timezone_identifiers_list(). * @return bool true on success + * @throws Exception */ public function set_site_locale(string $locale_id, $payload): bool { @@ -1665,13 +1762,14 @@ class Client } /** - * Update site snmp + * Update site snmp. * * @param string $snmp_id _id value of the snmp section * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * site. Must be a (partial) object/array structured in the same manner as is returned * by list_settings() for the section with the "snmp" key. * @return bool true on success + * @throws Exception */ public function set_site_snmp(string $snmp_id, $payload): bool { @@ -1681,13 +1779,14 @@ class Client } /** - * Update site mgmt + * Update site mgmt. * * @param string $mgmt_id _id value of the mgmt section * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * site. Must be a (partial) object/array structured in the same manner as is returned * by list_settings() for the section with the "mgmt" key. * @return bool true on success + * @throws Exception */ public function set_site_mgmt(string $mgmt_id, $payload): bool { @@ -1697,13 +1796,14 @@ class Client } /** - * Update site guest access + * Update site guest access. * * @param string $guest_access_id _id value of the guest_access section * @param object|array $payload stdClass object or associative array containing the configuration to apply * to the site. Must be a (partial) object/array structured in the same manner * as is returned by list_settings() for the section with the "guest_access" key. * @return bool true on success + * @throws Exception */ public function set_site_guest_access(string $guest_access_id, $payload): bool { @@ -1714,13 +1814,14 @@ class Client } /** - * Update site ntp + * Update site ntp. * * @param string $ntp_id _id value of the ntp section * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * site. Must be a (partial) object/array structured in the same manner as is returned * by list_settings() for the section with the "ntp" key. * @return bool true on success + * @throws Exception */ public function set_site_ntp(string $ntp_id, $payload): bool { @@ -1730,13 +1831,14 @@ class Client } /** - * Update site connectivity + * Update site connectivity. * * @param string $connectivity_id _id value of the connectivity section * @param object|array $payload stdClass object or associative array containing the configuration to apply * to the site. Must be a (partial) object/array structured in the same manner * as is returned by list_settings() for the section with the "connectivity" key. * @return bool true on success + * @throws Exception */ public function set_site_connectivity(string $connectivity_id, $payload): bool { @@ -1747,9 +1849,10 @@ class Client } /** - * Fetch admins + * Fetch admins. * * @return array|bool containing administrator objects for selected site + * @throws JsonDecodeException|LoginRequiredException */ public function list_admins() { @@ -1759,9 +1862,10 @@ class Client } /** - * Fetch all admins + * Fetch all admins. * * @return array|bool containing administrator objects for all sites + * @throws JsonDecodeException|LoginRequiredException */ public function list_all_admins() { @@ -1769,7 +1873,7 @@ class Client } /** - * Invite a new admin for access to the current site + * Invite a new admin for access to the current site. * * @note - after issuing a valid request, an invite is sent to the email address provided * - issuing this command against an existing admin triggers a "re-invite" @@ -1787,6 +1891,8 @@ class Client * restart devices, default value is false. With versions < 5.9.X this only applies * when readonly is true. * @return bool true on success + * @throws EmailInvalidException + * @throws Exception */ public function invite_admin( string $name, @@ -1797,12 +1903,10 @@ class Client bool $device_restart = false ): bool { - $email = trim($email); - $email_valid = filter_var($email, FILTER_VALIDATE_EMAIL); + $email = trim($email); - if (!$email_valid) { - trigger_error('The email address provided is invalid!'); - return false; + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new EmailInvalidException(); } $payload = [ @@ -1830,7 +1934,7 @@ class Client } /** - * Assign an existing admin to the current site + * Assign an existing admin to the current site. * * @param string $admin_id _id value of the admin user to assign, can be obtained using the * list_all_admins() method/function @@ -1844,6 +1948,7 @@ class Client * restart devices, default value is false. With versions < 5.9.X this only applies * when readonly is true. * @return bool true on success + * @throws Exception */ public function assign_existing_admin( string $admin_id, @@ -1875,7 +1980,7 @@ class Client } /** - * Update an admin of the current site + * Update an admin of the current site. * * @param string $admin_id _id value of the admin user to update, can be obtained using the * list_all_admins() method/function @@ -1892,6 +1997,8 @@ class Client * restart devices, default value is false. With versions < 5.9.X this only applies * when readonly is true. * @return bool true on success + * @throws EmailInvalidException + * @throws Exception */ public function update_admin( string $admin_id, @@ -1903,12 +2010,10 @@ class Client bool $device_restart = false ): bool { - $email = trim($email); - $email_valid = filter_var($email, FILTER_VALIDATE_EMAIL); + $email = trim($email); - if (!$email_valid) { - trigger_error('The email address provided is invalid!'); - return false; + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new EmailInvalidException(); } $payload = [ @@ -1937,12 +2042,13 @@ class Client } /** - * Revoke an admin from the current site + * Revoke an admin from the current site. * * @note only non-superadmin accounts can be revoked * @param string $admin_id _id value of the admin to revoke, can be obtained using the * list_all_admins() method/function * @return bool true on success + * @throws Exception */ public function revoke_admin(string $admin_id): bool { @@ -1952,12 +2058,13 @@ class Client } /** - * Delete an admin entirely + * Delete an admin entirely. * * @note Make sure the admin is revoked from all the sites before deleting * @param string $admin_id _id value of the admin to delete, can be obtained using the * list_all_admins() method/function * @return bool true on success + * @throws Exception */ public function delete_admin(string $admin_id): bool { @@ -1967,9 +2074,10 @@ class Client } /** - * Fetch WLAN groups + * Fetch WLAN groups. * * @return array|bool containing known wlan_groups + * @throws JsonDecodeException|LoginRequiredException */ public function list_wlan_groups() { @@ -1977,9 +2085,10 @@ class Client } /** - * Fetch sysinfo + * Fetch sysinfo. * - * @return array|bool containing known sysinfo data + * @return array containing known sysinfo data + * @throws JsonDecodeException|LoginRequiredException */ public function stat_sysinfo() { @@ -1987,47 +2096,50 @@ class Client } /** - * Fetch controller status + * Fetch controller status. * - * @note login is not required + * @note login is not required, except for UniFi OS-based controllers * @return bool true upon success (controller is online) + * @throws Exception */ public function stat_status(): bool { - return $this->fetch_results_boolean('/status', null, false); + return $this->fetch_results_boolean('/status', null, $this->is_unifi_os); } /** - * Fetch full controller status - * - * @note login is not required + * Fetch full controller status. * + * @note login is not required, except for UniFi OS-based controllers * @return bool|array status array upon success, false upon failure + * @throws Exception */ public function stat_full_status() { - $this->fetch_results_boolean('/status', null, false); + $this->fetch_results_boolean('/status', null, $this->is_unifi_os); return json_decode($this->get_last_results_raw()); } /** - * Fetch device name mappings + * Fetch device name mappings. * - * @note login is not required + * @note login is not required, except for UniFi OS-based controllers * @return bool|array mappings array upon success, false upon failure + * @throws Exception */ public function list_device_name_mappings() { - $this->fetch_results_boolean('/dl/firmware/bundles.json', null, false); + $this->fetch_results_boolean('/dl/firmware/bundles.json', null, $this->is_unifi_os); return json_decode($this->get_last_results_raw()); } /** - * Fetch self + * Fetch self. * * @return array|bool containing information about the logged-in user + * @throws JsonDecodeException|LoginRequiredException */ public function list_self() { @@ -2035,10 +2147,11 @@ class Client } /** - * Fetch vouchers + * Fetch vouchers. * * @param int|null $create_time optional, create time of the vouchers to fetch in Unix timestamp in seconds * @return array|bool containing hotspot voucher objects + * @throws JsonDecodeException|LoginRequiredException */ public function stat_voucher(int $create_time = null) { @@ -2048,10 +2161,11 @@ class Client } /** - * Fetch payments + * Fetch payments. * * @param int|null $within optional, number of hours to go back to fetch payments * @return array|bool containing hotspot payments + * @throws JsonDecodeException|LoginRequiredException */ public function stat_payment(int $within = null) { @@ -2061,12 +2175,13 @@ class Client } /** - * Create hotspot operator (using REST) + * Create a hotspot operator (using REST). * * @param string $name name for the hotspot operator * @param string $x_password clear text password for the hotspot operator * @param string $note optional, note to attach to the hotspot operator * @return bool true upon success + * @throws Exception */ public function create_hotspotop(string $name, string $x_password, string $note = ''): bool { @@ -2079,9 +2194,10 @@ class Client } /** - * Fetch hotspot operators (using REST) + * Fetch hotspot operators (using REST). * * @return array|bool containing hotspot operators + * @throws JsonDecodeException|LoginRequiredException */ public function list_hotspotop() { @@ -2089,7 +2205,7 @@ class Client } /** - * Create voucher(s) + * Create voucher(s). * * @note please use the stat_voucher() method/function to retrieve the newly created voucher(s) by create_time * @param int $minutes minutes the voucher is valid after activation (expiration time) @@ -2102,6 +2218,7 @@ class Client * @param int|null $megabytes data transfer limit in MB * @return array|bool containing a single object/array which contains the create_time(stamp) of the voucher(s) * created, false upon failure + * @throws JsonDecodeException|LoginRequiredException */ public function create_voucher( int $minutes, @@ -2140,10 +2257,11 @@ class Client } /** - * Revoke voucher + * Revoke voucher. * * @param string $voucher_id _id value of the voucher to revoke * @return bool true on success + * @throws Exception */ public function revoke_voucher(string $voucher_id): bool { @@ -2153,10 +2271,11 @@ class Client } /** - * Extend guest authorization + * Extend guest authorization. * * @param string $guest_id _id value of the guest to extend the authorization for * @return bool true on success + * @throws Exception */ public function extend_guest_validity(string $guest_id): bool { @@ -2165,9 +2284,10 @@ class Client } /** - * Fetch port forwarding stats + * Fetch port forwarding stats. * * @return array|bool containing port forwarding stats + * @throws JsonDecodeException|LoginRequiredException */ public function list_portforward_stats() { @@ -2175,9 +2295,10 @@ class Client } /** - * Fetch DPI stats + * Fetch DPI stats. * * @return array|bool containing DPI stats + * @throws JsonDecodeException|LoginRequiredException */ public function list_dpi_stats() { @@ -2185,13 +2306,14 @@ class Client } /** - * Fetch filtered DPI stats + * Fetch filtered DPI stats. * * @param string $type optional, whether to returns stats by app or by category, valid values: * 'by_cat' or 'by_app' * @param array|null $cat_filter optional, array containing numeric category ids to filter by, * only to be combined with a "by_app" value for $type * @return array|bool containing filtered DPI stats + * @throws JsonDecodeException|LoginRequiredException */ public function list_dpi_stats_filtered(string $type = 'by_cat', array $cat_filter = null) { @@ -2209,9 +2331,10 @@ class Client } /** - * Fetch current channels + * Fetch current channels. * * @return array|bool containing currently allowed channels + * @throws JsonDecodeException|LoginRequiredException */ public function list_current_channels() { @@ -2219,12 +2342,13 @@ class Client } /** - * Fetch country codes + * Fetch country codes. * * @note these codes following the ISO standard: * https://en.wikipedia.org/wiki/ISO_3166-1_numeric * * @return array|bool containing available country codes + * @throws JsonDecodeException|LoginRequiredException */ public function list_country_codes() { @@ -2232,9 +2356,10 @@ class Client } /** - * Fetch port forwarding settings + * Fetch port forwarding settings. * * @return array|bool containing port forwarding settings + * @throws JsonDecodeException|LoginRequiredException */ public function list_portforwarding() { @@ -2242,9 +2367,10 @@ class Client } /** - * Fetch port configurations + * Fetch port configurations. * * @return array|bool containing port configurations + * @throws JsonDecodeException|LoginRequiredException */ public function list_portconf() { @@ -2252,9 +2378,10 @@ class Client } /** - * Fetch VoIP extensions + * Fetch VoIP extensions. * * @return array|bool containing VoIP extensions + * @throws JsonDecodeException|LoginRequiredException */ public function list_extension() { @@ -2262,9 +2389,10 @@ class Client } /** - * Fetch site settings + * Fetch site settings. * * @return array|bool containing site configuration settings + * @throws JsonDecodeException|LoginRequiredException */ public function list_settings() { @@ -2272,10 +2400,11 @@ class Client } /** - * Adopt a device to the selected site + * Adopt a device to the current site. * * @param string|array $macs device MAC address or an array of MAC addresses * @return bool true on success + * @throws Exception */ public function adopt_device($macs): bool { @@ -2288,7 +2417,7 @@ class Client } /** - * Adopt a device using custom SSH credentials + * Adopt a device using custom SSH credentials. * * @param string $mac device MAC address * @param string $ip IP to use for SSH connection @@ -2298,6 +2427,7 @@ class Client * @param int $port optional, SSH port * @param bool $ssh_key_verify optional, if true, verify the SSH key for the device * @return bool true on success + * @throws Exception */ public function advanced_adopt_device( string $mac, @@ -2329,6 +2459,7 @@ class Client * @param string|array $macs single device MAC address string or an array of MAC addresses * @param string $inform_url inform URL to point the device to (e.g., http://10.1.0.10:9080/inform) * @return bool true on success + * @throws Exception */ public function migrate_device($macs, string $inform_url): bool { @@ -2346,6 +2477,7 @@ class Client * * @param string|array $macs single device MAC address string or an array of MAC addresses * @return bool true on success + * @throws Exception */ public function cancel_migrate_device($macs): bool { @@ -2367,6 +2499,7 @@ class Client * capable ports. Keep in mind that a 'hard' reboot * - does *NOT* trigger a factory-reset. * @return bool true on success + * @throws Exception */ public function restart_device($macs, string $reboot_type = 'soft'): bool { @@ -2388,6 +2521,7 @@ class Client * @note unclear whether this function is still supported across all controller versions * @param string|array $mac single device MAC address string or an array of MAC addresses * @return bool true on success + * @throws Exception */ public function force_provision($mac): bool { @@ -2400,10 +2534,11 @@ class Client } /** - * Reboot a UniFi CloudKey + * Reboot a UniFi CloudKey. * * @note this API call has no effect on UniFi controllers *not* running on a UniFi OS device * @return bool true on success + * @throws Exception */ public function reboot_cloudkey(): bool { @@ -2413,7 +2548,7 @@ class Client } /** - * Disable/enable an access point (using REST) + * Disable/enable an access point (using REST). * * @note - a disabled device is excluded from the dashboard status and device count and its LED and WLAN are turned off * - appears to only be supported for access points @@ -2422,6 +2557,7 @@ class Client * @param string $ap_id value of _id for the access point which can be obtained from the device list * @param bool $disable true disables the device, false enables the device * @return bool true on success + * @throws Exception */ public function disable_ap(string $ap_id, bool $disable): bool { @@ -2433,7 +2569,7 @@ class Client } /** - * Override LED mode for a device (using REST) + * Override LED mode for a device (using REST). * * @note available since controller versions 5.2.X * @param string $device_id value of _id for the device which can be obtained from the device list @@ -2441,6 +2577,7 @@ class Client * "on" enables the LED of the device, * "default" applies the site-wide setting for device LEDs * @return bool true on success + * @throws Exception */ public function led_override(string $device_id, string $override_mode): bool { @@ -2456,12 +2593,13 @@ class Client } /** - * Toggle flashing LED of an access point for locating purposes + * Toggle flashing LED of an access point for locating purposes. * * @note replaces the old set_locate_ap() and unset_locate_ap() methods/functions * @param string $mac device MAC address * @param bool $enable true enables flashing LED, false disables flashing LED * @return bool true on success + * @throws Exception */ public function locate_ap(string $mac, bool $enable): bool { @@ -2473,10 +2611,11 @@ class Client } /** - * Toggle LEDs of all the access points ON or OFF + * Toggle LEDs of all the access points ON or OFF. * * @param bool $enable true switches LEDs of all the access points ON, false switches them OFF * @return bool true on success + * @throws Exception */ public function site_leds(bool $enable): bool { @@ -2486,7 +2625,7 @@ class Client } /** - * Update access point radio settings + * Update access point radio settings. * * @note only supported on pre-5.X.X controller versions * @param string $ap_id the "_id" value for the access point you wish to update @@ -2496,6 +2635,7 @@ class Client * @param string $tx_power_mode power level, "low", "medium", or "high" * @param int $tx_power transmit power level, default=0 * @return bool true on success + * @throws Exception */ public function set_ap_radiosettings(string $ap_id, string $radio, int $channel, int $ht, string $tx_power_mode, int $tx_power): bool { @@ -2513,12 +2653,13 @@ class Client } /** - * Assign access point to another WLAN group + * Assign access point to another WLAN group. * * @param string $type_id WLAN type, can be either 'ng' (for WLANs 2G (11n/b/g)) or 'na' (WLANs 5G (11n/a/ac)) * @param string $device_id _id value of the access point to be modified * @param string $group_id _id value of the WLAN group to assign device to * @return bool true on success + * @throws Exception */ public function set_ap_wlangroup(string $type_id, string $device_id, string $group_id): bool { @@ -2535,7 +2676,7 @@ class Client } /** - * Update guest login settings + * Update guest login settings. * * @note both portal parameters are set to the same value! * @param bool $portal_enabled enable/disable the captive portal @@ -2548,6 +2689,7 @@ class Client * @param string $section_id value of _id for the site settings section where key = "guest_access", settings * can be obtained using the list_settings() function * @return bool true on success + * @throws Exception */ public function set_guestlogin_settings( bool $portal_enabled, @@ -2576,13 +2718,14 @@ class Client } /** - * Update guest login settings, base + * Update guest login settings, base. * * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * guest logins, must be a (partial) object/array structured in the same manner as is * returned by list_settings() for the "guest_access" section. * @param string $section_id * @return bool true on success + * @throws Exception */ public function set_guestlogin_settings_base($payload, string $section_id = ''): bool { @@ -2590,17 +2733,17 @@ class Client $section_id = '/' . $section_id; } - return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/guest_access' . $section_id, - $payload); + return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/guest_access' . $section_id, $payload); } /** - * Update IPS/IDS settings, base + * Update IPS/IDS settings, base. * * @param object|array $payload stdClass object or associative array containing the IPS/IDS settings to apply, must * be a (partial) object/array structured in the same manner as is returned by * list_settings() for the "ips" section. * @return bool true on success + * @throws Exception */ public function set_ips_settings_base($payload): bool { @@ -2608,7 +2751,7 @@ class Client } /** - * Update "Super Management" settings, base + * Update "Super Management" settings, base. * * @param string $settings_id value of _id for the site settings section where key = "super_mgmt", settings * can be obtained using the list_settings() function @@ -2616,15 +2759,15 @@ class Client * to apply, must be a (partial) object/array structured in the same manner as is * returned by list_settings() for the "super_mgmt" section. * @return bool true on success + * @throws Exception */ public function set_super_mgmt_settings_base(string $settings_id, $payload): bool { - return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_mgmt/' . trim($settings_id), - $payload); + return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_mgmt/' . trim($settings_id), $payload); } /** - * Update "Super SMTP" settings, base + * Update "Super SMTP" settings, base. * * @param string $settings_id value of _id for the site settings section where key = "super_smtp", settings * can be obtained using the list_settings() function @@ -2632,11 +2775,11 @@ class Client * apply, must be a (partial) object/array structured in the same manner as is * returned by list_settings() for the "super_smtp" section. * @return bool true on success + * @throws Exception */ public function set_super_smtp_settings_base(string $settings_id, $payload): bool { - return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_smtp/' . trim($settings_id), - $payload); + return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_smtp/' . trim($settings_id), $payload); } /** @@ -2648,19 +2791,20 @@ class Client * settings to apply, must be a (partial) object/array structured in the same * manner as is returned by list_settings() for the "super_identity" section. * @return bool true on success + * @throws Exception */ public function set_super_identity_settings_base(string $settings_id, $payload): bool { - return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_identity/' . trim($settings_id), - $payload); + return $this->fetch_results_boolean('/api/s/' . $this->site . '/set/setting/super_identity/' . trim($settings_id), $payload); } /** - * Rename access point + * Rename an access point. * * @param string $ap_id _id of the access point to rename * @param string $ap_name new name to assign to the access point * @return bool true on success + * @throws Exception */ public function rename_ap(string $ap_id, string $ap_name): bool { @@ -2670,11 +2814,12 @@ class Client } /** - * Move a device to another site + * Move a device to another site. * * @param string $mac MAC address of the device to move * @param string $site_id _id (24 char string) of the site to move the device to * @return bool true on success + * @throws Exception */ public function move_device(string $mac, string $site_id): bool { @@ -2688,10 +2833,11 @@ class Client } /** - * Delete a device from the current site + * Delete a device from the current site. * * @param string $mac MAC address of the device to delete * @return bool true on success + * @throws Exception */ public function delete_device(string $mac): bool { @@ -2704,9 +2850,10 @@ class Client } /** - * Fetch dynamic DNS settings (using REST) + * Fetch dynamic DNS settings (using REST). * * @return array|bool containing dynamic DNS settings + * @throws JsonDecodeException|LoginRequiredException */ public function list_dynamicdns() { @@ -2714,13 +2861,14 @@ class Client } /** - * Create dynamic DNS settings, base (using REST) + * Create dynamic DNS settings, base (using REST). * * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * site, must be a * (partial) object/array structured in the same manner as is returned by * list_dynamicdns() for the site. * @return bool true on success + * @throws Exception */ public function create_dynamicdns($payload): bool { @@ -2728,7 +2876,7 @@ class Client } /** - * Update site dynamic DNS, base (using REST) + * Update site dynamic DNS, base (using REST). * * @param string $dynamicdns_id _id of the settings which can be found with the list_dynamicdns() function * @param object|array $payload stdClass object or associative array containing the configuration to apply to @@ -2736,6 +2884,7 @@ class Client * (partial) object/array structured in the same manner as is returned by * list_dynamicdns() for the site. * @return bool true on success + * @throws Exception */ public function set_dynamicdns(string $dynamicdns_id, $payload): bool { @@ -2746,10 +2895,11 @@ class Client } /** - * Fetch network settings (using REST) + * Fetch network settings (using REST). * * @param string $network_id optional, _id value of the network to get settings for * @return array|bool containing (non-wireless) networks and their settings + * @throws JsonDecodeException|LoginRequiredException */ public function list_networkconf(string $network_id = '') { @@ -2757,13 +2907,14 @@ class Client } /** - * Create a network (using REST) + * Create a network (using REST). * * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * network, must be a (partial) object structured in the same manner as is returned by * list_networkconf() for the specific network type. Do not include the _id property, * it is assigned by the controller and returned upon success. * @return array|bool containing a single object with details of the new network on success, else returns false + * @throws JsonDecodeException|LoginRequiredException */ public function create_network($payload) { @@ -2771,13 +2922,14 @@ class Client } /** - * Update network settings, base (using REST) + * Update network settings, base (using REST). * * @param string $network_id the "_id" value for the network you wish to update * @param object|array $payload stdClass object or associative array containing the configuration to apply to * the network, must be a (partial) object/array structured in the same manner as * is returned by list_networkconf() for the network. * @return bool true on success + * @throws Exception */ public function set_networksettings_base(string $network_id, $payload): bool { @@ -2788,10 +2940,11 @@ class Client } /** - * Delete a network (using REST) + * Delete a network (using REST). * * @param string $network_id _id value of the network which can be found with the list_networkconf() function * @return bool true on success + * @throws Exception */ public function delete_network(string $network_id): bool { @@ -2801,11 +2954,12 @@ class Client } /** - * Fetch wlan settings (using REST) + * Fetch wlan settings (using REST). * * @param string $wlan_id optional, _id value of the wlan to fetch the settings for * @return array|bool containing wireless networks and their settings, or an array containing a single wireless network * when using the parameter + * @throws JsonDecodeException|LoginRequiredException */ public function list_wlanconf(string $wlan_id = '') { @@ -2813,7 +2967,7 @@ class Client } /** - * Create a WLAN + * Create a WLAN. * * @param string $name SSID * @param string $x_passphrase new pre-shared key, minimal length is 8 characters, maximum length is 63, @@ -2836,6 +2990,7 @@ class Client * and higher * @param array $payload optional, array of additional parameters (wlan_bands, wpa3_support, etc.) * @return bool true on success + * @throws Exception */ public function create_wlan( string $name, @@ -2888,13 +3043,14 @@ class Client } /** - * Update wlan settings, base (using REST) + * Update wlan settings, base (using REST). * * @param string $wlan_id the "_id" value for the WLAN which can be found with the list_wlanconf() function * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * wlan, must be a (partial) object/array structured in the same manner as is returned * by list_wlanconf() for the wlan. * @return bool true on success + * @throws Exception */ public function set_wlansettings_base(string $wlan_id, $payload): bool { @@ -2904,13 +3060,14 @@ class Client } /** - * Update basic wlan settings + * Update basic wlan settings. * * @param string $wlan_id the "_id" value for the WLAN which can be found with the list_wlanconf() function * @param string $x_passphrase new pre-shared key, minimal length is 8 characters, maximum length is 63, * is ignored if set to null * @param string $name optional, SSID * @return bool true on success + * @throws Exception */ public function set_wlansettings(string $wlan_id, string $x_passphrase, string $name = ''): bool { @@ -2930,6 +3087,7 @@ class Client * @param string $wlan_id the "_id" value for the WLAN which can be found with the list_wlanconf() function * @param bool $disable true disables the wlan, false enables it * @return bool true on success + * @throws Exception */ public function disable_wlan(string $wlan_id, bool $disable): bool { @@ -2940,10 +3098,11 @@ class Client } /** - * Delete a wlan (using REST) + * Delete a wlan (using REST). * * @param string $wlan_id the "_id" value for the WLAN which can be found with the list_wlanconf() function * @return bool true on success + * @throws Exception */ public function delete_wlan(string $wlan_id): bool { @@ -2952,7 +3111,7 @@ class Client } /** - * Update MAC filter for a wlan + * Update MAC filter for a wlan. * * @param string $wlan_id the "_id" value for the WLAN which can be found with the list_wlanconf() * function @@ -2962,6 +3121,7 @@ class Client * replacing existing values. Existing MAC filter list can be obtained * through list_wlanconf(). * @return bool true on success + * @throws Exception */ public function set_wlan_mac_filter(string $wlan_id, string $mac_filter_policy, bool $mac_filter_enabled, array $macs): bool { @@ -2981,13 +3141,14 @@ class Client } /** - * Fetch events + * Fetch events. * * @param integer $historyhours optional, hours to go back, default value is 720 hours * @param integer $start optional, which event number to start with (useful for paging of results), default * value is 0 * @param integer $limit optional, number of events to return, default value is 3000 * @return array|bool containing known events + * @throws JsonDecodeException|LoginRequiredException */ public function list_events(int $historyhours = 720, int $start = 0, int $limit = 3000) { @@ -3003,12 +3164,13 @@ class Client } /** - * Fetch alarms + * Fetch alarms. * * @param array $payload optional, array of flags to filter by * Example: ["archived" => false, "key" => "EVT_GW_WANTransition"] * return only unarchived for a specific key * @return array|bool containing known alarms + * @throws JsonDecodeException|LoginRequiredException */ public function list_alarms(array $payload = []) { @@ -3016,11 +3178,12 @@ class Client } /** - * Count alarms + * Count alarms. * * @param bool|null $archived optional, if true all alarms are counted, if false only non-archived (active) alarms are * counted, by default all alarms are counted * @return array|bool containing the alarm count + * @throws JsonDecodeException|LoginRequiredException */ public function count_alarms(bool $archived = null) { @@ -3030,11 +3193,12 @@ class Client } /** - * Archive alarms(s) + * Archive alarms(s). * * @param string $alarm_id optional, _id of the alarm to archive which can be found with the list_alarms() function, * by default all alarms are archived * @return bool true on success + * @throws Exception */ public function archive_alarm(string $alarm_id = ''): bool { @@ -3048,11 +3212,12 @@ class Client } /** - * Check controller update + * Check controller update. * * @note triggers an update of the controller's cached, latest known version. * @return array|bool returns an array with a single object containing details of the current known latest * controller version info on success, else returns false + * @throws JsonDecodeException|LoginRequiredException */ public function check_controller_update() { @@ -3060,10 +3225,11 @@ class Client } /** - * Check firmware update + * Check firmware update. * * @note triggers a Device Firmware Update in Classic Settings > System settings > Maintenance * @return bool true upon success + * @throws Exception */ public function check_firmware_update(): bool { @@ -3073,11 +3239,12 @@ class Client } /** - * Upgrade a device to the latest firmware + * Upgrade a device to the latest firmware. * * @note updates the device to the latest STABLE firmware known to the controller * @param string $mac MAC address of the device to upgrade * @return bool true upon success + * @throws Exception */ public function upgrade_device(string $mac): bool { @@ -3087,11 +3254,12 @@ class Client } /** - * Upgrade all devices of a certain type to the latest firmware + * Upgrade all devices of a certain type to the latest firmware. * * @note updates all devices of the selected type to the latest STABLE firmware known to the controller * @param string $type the type of devices to upgrade, must be one of "uap", "usw", "ugw". "uap" is the default. * @return bool true upon success + * @throws Exception */ public function upgrade_all_devices(string $type = 'uap'): bool { @@ -3101,13 +3269,14 @@ class Client } /** - * Upgrade a device to a specific firmware file + * Upgrade a device to a specific firmware file. * * @note - updates the device to the firmware file at the given URL * - please take great care to select a valid firmware file for the device! * @param string $firmware_url URL for the firmware file to upgrade the device to * @param string|array $macs MAC address of the device to upgrade or an array of MAC addresses * @return bool true upon success + * @throws Exception */ public function upgrade_device_external(string $firmware_url, $macs): bool { @@ -3120,12 +3289,13 @@ class Client } /** - * Start rolling upgrade. + * Start a rolling upgrade. * * @note upgrades all UniFi devices to the latest firmware known to the controller in a * staggered/rolling fashion * @param array $payload optional, array of device types to upgrade, default is all device types * @return bool true upon success + * @throws Exception */ public function start_rolling_upgrade(array $payload = ['uap', 'usw', 'ugw', 'uxg']): bool { @@ -3133,9 +3303,10 @@ class Client } /** - * Cancel rolling upgrade. + * Cancel a rolling upgrade. * * @return bool true upon success + * @throws Exception */ public function cancel_rolling_upgrade(): bool { @@ -3150,6 +3321,7 @@ class Client * @param string $type optional, "available" or "cached", determines which firmware types to return, * default value is "available" * @return array|bool containing firmware versions + * @throws JsonDecodeException|LoginRequiredException */ public function list_firmware(string $type = 'available') { @@ -3163,13 +3335,14 @@ class Client } /** - * Power-cycle the PoE output of a switch port + * Power-cycle the PoE output of a switch port. * * @note - only applies to switches and their PoE ports * - the port must be actually providing power * @param string $mac main MAC address of the switch * @param int $port_idx port number/index of the port to be affected * @return bool true upon success + * @throws Exception */ public function power_cycle_switch_port(string $mac, int $port_idx): bool { @@ -3179,10 +3352,11 @@ class Client } /** - * Trigger an RF scan by an AP + * Trigger an RF scan by an AP. * * @param string $mac MAC address of the AP * @return bool true upon success + * @throws Exception */ public function spectrum_scan(string $mac): bool { @@ -3191,11 +3365,12 @@ class Client } /** - * Check the RF scanning state of an AP + * Check the RF scanning state of an AP. * * @param string $mac MAC address of the AP * @return array|bool containing relevant information (results if available) regarding the RF scanning state of the * AP + * @throws JsonDecodeException|LoginRequiredException */ public function spectrum_scan_state(string $mac) { @@ -3203,13 +3378,14 @@ class Client } /** - * Update device settings, base (using REST) + * Update device settings, base (using REST). * * @param string $device_id _id of the device which can be found with the list_devices() function * @param object|array $payload stdClass object or associative array containing the configuration to apply to the * device, must be a (partial) object/array structured in the same manner as is returned * by list_devices() for the device. * @return bool true on success + * @throws Exception */ public function set_device_settings_base(string $device_id, $payload): bool { @@ -3219,10 +3395,11 @@ class Client } /** - * Fetch Radius profiles (using REST) + * Fetch Radius profiles (using REST). * * @note this function/method is only supported on controller versions 5.5.19 and later * @return array|bool objects containing all Radius profiles for the current site + * @throws JsonDecodeException|LoginRequiredException */ public function list_radius_profiles() { @@ -3230,10 +3407,11 @@ class Client } /** - * Fetch Radius user accounts (using REST) + * Fetch Radius user accounts (using REST). * * @note this function/method is only supported on controller versions 5.5.19 and later * @return array|bool objects containing all Radius accounts for the current site + * @throws JsonDecodeException|LoginRequiredException */ public function list_radius_accounts() { @@ -3241,7 +3419,7 @@ class Client } /** - * Create a Radius user account (using REST) + * Create a Radius user account (using REST). * * @note this function/method is only supported on controller versions 5.5.19 and later * @param string $name name for the new account @@ -3278,6 +3456,7 @@ class Client * 15 E.164 with NSAP format subaddress * @param string|null $vlan optional, VLAN to assign to the account * @return bool|array containing a single object for the newly created account upon success, else returns false + * @throws JsonDecodeException|LoginRequiredException */ public function create_radius_account( string $name, @@ -3319,7 +3498,7 @@ class Client } /** - * Update a Radius account, base (using REST) + * Update a Radius account, base (using REST). * * @note this function/method is only supported on controller versions 5.5.19 and later * @param string $account_id _id of the account which can be found with the list_radius_accounts() function @@ -3327,6 +3506,7 @@ class Client * account, must be a (partial) object/array structured in the same manner as is * returned by list_radius_accounts() for the account. * @return bool true on success + * @throws Exception */ public function set_radius_account_base(string $account_id, $payload): bool { @@ -3336,11 +3516,12 @@ class Client } /** - * Delete a Radius account (using REST) + * Delete a Radius account (using REST). * * @note this function/method is only supported on controller versions 5.5.19 and later * @param string $account_id _id of the account which can be found with the list_radius_accounts() function * @return bool true on success + * @throws Exception */ public function delete_radius_account(string $account_id): bool { @@ -3349,12 +3530,13 @@ class Client } /** - * Execute specific stats command + * Execute specific stats command. * * @param string $command command to execute, known valid values: * 'reset-dpi', resets all DPI counters for the current site, * to be extended in the future * @return bool true on success + * @throws Exception */ public function cmd_stat(string $command): bool { @@ -3368,10 +3550,11 @@ class Client } /** - * Toggle Element Adoption ON or OFF + * Toggle Element Adoption ON or OFF. * * @param bool $enable true enables Element Adoption, false disables Element Adoption * @return bool true on success + * @throws Exception */ public function set_element_adoption(bool $enable): bool { @@ -3394,10 +3577,11 @@ class Client * @param int $page_size optional, number of entries to fetch per page, default value is 100 * @param array $custom_payload optional, an array of additional parameters to pass with the request. Is merged * with the default payload array constructed by this method using array_merge(). - * @return array|bool array containing results from the selected system log section/class, false on error. + * @return array array containing results from the selected system log section/class. * The 'data' key in the returned array contains the actual system log entries. * The returned array also contains the page number and size, and the total number of entries * available. + * @throws JsonDecodeException|LoginRequiredException */ public function get_system_log(string $class = 'device-alert', int $start = null, int $end = null, int $page_number = 0, int $page_size = 100, array $custom_payload = []) { @@ -3438,7 +3622,7 @@ class Client } /** - * List device states + * List device states. * * @note this function returns a partial implementation of the codes listed at this URL: * @see https://help.ui.com/hc/en-us/articles/205231710-UniFi-UAP-Status-Meaning-Definitions @@ -3461,7 +3645,7 @@ class Client } /** - * Custom API request + * Custom API request. * * @note Only use this method when you fully understand the behavior of the UniFi controller API. * No input validation performed, to be used with care! @@ -3472,6 +3656,7 @@ class Client * @param string $return optional, string; determines how to return results, when "boolean" the method must * return a boolean result or "array" when the method must return an array (default) * @return bool|array returns results as requested, returns false on incorrect parameters + * @throws JsonDecodeException|LoginRequiredException|Exception */ public function custom_api_request(string $path, string $method = 'GET', $payload = null, string $return = 'array') { @@ -3497,103 +3682,55 @@ class Client } /**************************************************************** - * "Aliases" for deprecated functions from here, used to support - * backward compatibility: + * Deprecated functions from here, Exceptions will be thrown ****************************************************************/ /** - * Fetch access points and other devices under management of the controller (USW, USG, and/or UnIfi OS consoles) - * - * @note changed function/method name to fit its purpose - * @param string|null $mac optional, the MAC address of a single device for which the call must be made - * @return array|bool containing known device objects (or a single device when using the parameter) + * @throws MethodDeprecatedException */ - public function list_aps(string $mac = null) + public function list_aps() { - trigger_error( - 'Function list_aps() has been deprecated, use list_devices() instead.', - E_USER_DEPRECATED - ); - - return $this->list_devices($mac); + throw new MethodDeprecatedException('Function list_aps() has been deprecated, use list_devices() instead.'); } /** - * Start flashing LED of an access point for locating purposes - * - * @param string $mac device MAC address - * @return bool true on success + * @throws MethodDeprecatedException */ - public function set_locate_ap(string $mac): bool + public function set_locate_ap() { - trigger_error( - 'Function set_locate_ap() has been deprecated, use locate_ap() instead.', - E_USER_DEPRECATED - ); - - return $this->locate_ap($mac, true); + throw new MethodDeprecatedException('Function set_locate_ap() has been deprecated, use locate_ap() instead.'); } /** - * Stop flashing LED of an access point for locating purposes - * - * @param string $mac device MAC address - * @return bool true on success + * @throws MethodDeprecatedException */ - public function unset_locate_ap(string $mac): bool + public function unset_locate_ap(): bool { - trigger_error( - 'Function unset_locate_ap() has been deprecated, use locate_ap() instead.', - E_USER_DEPRECATED - ); - - return $this->locate_ap($mac, false); + throw new MethodDeprecatedException('Function unset_locate_ap() has been deprecated, use locate_ap() instead.'); } /** - * Switch LEDs of all the access points ON - * - * @return bool true on success + * @throws MethodDeprecatedException */ public function site_ledson(): bool { - trigger_error( - 'Function site_ledson() has been deprecated, use site_leds() instead.', - E_USER_DEPRECATED - ); - - return $this->site_leds(true); + throw new MethodDeprecatedException('Function site_ledson() has been deprecated, use site_leds() instead.'); } /** - * Switch LEDs of all the access points OFF - * - * @return bool true on success + * @throws MethodDeprecatedException */ public function site_ledsoff(): bool { - trigger_error( - 'Function site_ledsoff() has been deprecated, use site_leds() instead.', - E_USER_DEPRECATED - ); - - return $this->site_leds(false); + throw new MethodDeprecatedException('Function site_ledsoff() has been deprecated, use site_leds() instead.'); } /** - * Reboot an access point - * - * @param string $mac device MAC address - * @return bool true on success + * @throws MethodDeprecatedException */ - public function restart_ap(string $mac): bool + public function restart_ap(): bool { - trigger_error( - 'Function restart_ap() has been deprecated, use restart_device() instead.', - E_USER_DEPRECATED - ); - - return $this->restart_device($mac); + throw new MethodDeprecatedException('Function restart_ap() has been deprecated, use restart_device() instead.'); } /**************************************************************** @@ -3618,6 +3755,7 @@ class Client * @param string $site must be the short site name of a site to which the * provided credentials have access * @return string the new (short) site name + * @throws InvalidSiteNameException */ public function set_site(string $site): string { @@ -3694,13 +3832,12 @@ class Client /** * Get Cookie from UniFi controller (singular and plural for backward compatibility). * - * @note When the results from this method are stored in $_SESSION[$this->unificookie_name], the Class initially - * does not log in to the controller when a subsequent request is made using a new instance. - * This speeds up the overall request considerably. - * Only when a subsequent request fails (e.g. cookies have expired) will a new login be - * executed and the value of $_SESSION[$this->unificookie_name] is updated. - * To force the Class instance to log out automatically upon destruct, simply call logout() or unset - * $_SESSION[$this->unificookie_name] at the end of your code + * @note - When the results from this method are stored in $_SESSION[$this->unificookie_name], the Class initially + * does not log in to the controller when a subsequent request is made using a new instance. + * - This speeds up the overall request considerably. + * - Only when the subsequent request fails (e.g. cookies have expired) will a new login be + * executed and the value of $_SESSION[$this->unificookie_name] is updated. + * - To force the Class instance to log out automatically upon destruct, call logout() or unset. * @return string the UniFi controller cookie */ public function get_cookie(): string @@ -3758,17 +3895,18 @@ class Client * Set request method. * * @param string $curl_method a valid HTTP request method - * @return bool whether the request was successful or not + * @return string the new value of the private property $curl_method + * @throws InvalidCurlMethodException when an invalid cURL method is provided */ - public function set_curl_method(string $curl_method): bool + public function set_curl_method(string $curl_method): string { if (!in_array($curl_method, self::CURL_METHODS_ALLOWED)) { - return false; + throw new InvalidCurlMethodException(); } $this->curl_method = $curl_method; - return true; + return $this->curl_method; } /** @@ -3899,7 +4037,7 @@ class Client } /** - * Set value for the private property $curl_http_version. + * Set the value for the private property $curl_http_version. * * @note As of cURL version 7.62.0 the default value is CURL_HTTP_VERSION_2TLS which may cause issues, * this method allows you to set the value to CURL_HTTP_VERSION_1_1 when needed. @@ -3916,7 +4054,7 @@ class Client } /** - * Get current value of the private property $curl_http_version. + * Get the current value of the private property $curl_http_version. * * @return int the current value of $request_timeout, can be CURL_HTTP_VERSION_1_1 int(2) or * CURL_HTTP_VERSION_2TLS int(4) @@ -3940,7 +4078,8 @@ class Client * @param boolean $boolean optional, whether the method should return a boolean result, else return * the "data" array * @param boolean $login_required optional, whether the method requires to be logged in or not - * @return bool|array returns an array with the "data" array on success, else returns false + * @return bool|array returns an array with the "data" array on success, else returns boolean if $boolean is true + * @throws JsonDecodeException|LoginRequiredException|Exception|LoginRequiredException */ protected function fetch_results( string $path, @@ -3951,14 +4090,14 @@ class Client { /** Guard clause to check if logged in when needed. */ if ($login_required && !$this->is_logged_in) { - return false; + throw new LoginRequiredException(); } $this->last_results_raw = $this->exec_curl($path, $payload); if (is_string($this->last_results_raw)) { $response = json_decode($this->last_results_raw); - $this->catch_json_last_error(); + $this->get_json_last_error(); if (isset($response->meta->rc)) { if ($response->meta->rc === 'ok') { @@ -3970,31 +4109,30 @@ class Client return true; } + /** + * An error occurred + */ if ($response->meta->rc === 'error') { - /** - * An error occurred: - * set $this->set last_error_message if the returned error message is available - */ + $this->last_error_message = 'An unknown error was returned by the controller.'; + + /** set $this->set last_error_message if the returned error message is available */ if (isset($response->meta->msg)) { $this->last_error_message = $response->meta->msg; - if ($this->debug) { - trigger_error('Debug: Last error message: ' . $this->last_error_message); - } } + + throw new Exception('Error message: ' . $this->last_error_message); } } /** Deal with a response coming from the new v2 API. */ if (strpos($path, '/v2/api/') === 0) { if (isset($response->errorCode)) { + $this->last_error_message = 'An unknown error was returned by an API v2 endpoint.'; if (isset($response->message)) { $this->last_error_message = $response->message; - if ($this->debug) { - trigger_error('Debug: Last error message: ' . $this->last_error_message); - } } - return false; + throw new Exception('Error code: ' . $response->errorCode . ', message: ' . $this->last_error_message); } return $response; @@ -4012,6 +4150,7 @@ class Client * request * @param bool $login_required optional, whether the method requires to be logged in or not * @return bool [description] + * @throws Exception|JsonDecodeException|LoginRequiredException */ protected function fetch_results_boolean(string $path, $payload = null, bool $login_required = true): bool { @@ -4019,79 +4158,73 @@ class Client } /** - * Capture the latest JSON error when $this->debug is true. + * Capture the latest JSON error and throw an Exception. * - * @return bool true upon success, false upon failure + * @return bool true, if no error occurred + * @throws JsonDecodeException|LoginRequiredException when a JSON error occurred */ - protected function catch_json_last_error(): bool + protected function get_json_last_error(): bool { - if ($this->debug) { - $error = 'Unknown JSON error occurred'; - switch (json_last_error()) { - case JSON_ERROR_NONE: - /** JSON is valid, no error has occurred and return true early */ - return true; - case JSON_ERROR_DEPTH: - $error = 'The maximum stack depth has been exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $error = 'Invalid or malformed JSON'; - break; - case JSON_ERROR_CTRL_CHAR: - $error = 'Control character error, possibly incorrectly encoded'; - break; - case JSON_ERROR_SYNTAX: - $error = 'Syntax error, malformed JSON'; - break; - case JSON_ERROR_UTF8: - /** PHP >= 5.3.3 */ - $error = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - case JSON_ERROR_RECURSION: - /** PHP >= 5.5.0 */ - $error = 'One or more recursive references in the value to be encoded'; - break; - case JSON_ERROR_INF_OR_NAN: - /** PHP >= 5.5.0 */ - $error = 'One or more NAN or INF values in the value to be encoded'; - break; - case JSON_ERROR_UNSUPPORTED_TYPE: - $error = 'A value of a type that cannot be encoded was given'; - break; - } - - /** Check whether we have PHP >= 7.0.0. */ - if (defined('JSON_ERROR_INVALID_PROPERTY_NAME') && defined('JSON_ERROR_UTF16')) { - switch (json_last_error()) { - case JSON_ERROR_INVALID_PROPERTY_NAME: - $error = 'A property name that cannot be encoded was given'; - break; - case JSON_ERROR_UTF16: - $error = 'Malformed UTF-16 characters, possibly incorrectly encoded'; - break; - } - } - - trigger_error('JSON decode error: ' . $error); - - return false; + $error = 'Unknown JSON error occurred'; + switch (json_last_error()) { + case JSON_ERROR_NONE: + /** JSON is valid, no error has occurred and return true early */ + return true; + case JSON_ERROR_DEPTH: + $error = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $error = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_CTRL_CHAR: + $error = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_SYNTAX: + $error = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + /** PHP >= 5.3.3 */ + $error = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + case JSON_ERROR_RECURSION: + /** PHP >= 5.5.0 */ + $error = 'One or more recursive references in the value to be encoded'; + break; + case JSON_ERROR_INF_OR_NAN: + /** PHP >= 5.5.0 */ + $error = 'One or more NAN or INF values in the value to be encoded'; + break; + case JSON_ERROR_UNSUPPORTED_TYPE: + $error = 'A value of a type that cannot be encoded was given'; + break; } - return true; + /** Check whether we have PHP >= 7.0.0. */ + if (defined('JSON_ERROR_INVALID_PROPERTY_NAME') && defined('JSON_ERROR_UTF16')) { + switch (json_last_error()) { + case JSON_ERROR_INVALID_PROPERTY_NAME: + $error = 'A property name that cannot be encoded was given'; + break; + case JSON_ERROR_UTF16: + $error = 'Malformed UTF-16 characters, possibly incorrectly encoded'; + break; + } + } + + throw new JsonDecodeException('JSON decode error: ' . $error); } /** * Validate the submitted base URL. * * @param string $baseurl the base URL to validate - * @return bool true if base URL is a valid URL, else returns false + * @return bool true if base URL is a valid URL + * @throws InvalidBaseUrlException when an invalid base URL is provided */ protected function check_base_url(string $baseurl): bool { if (!filter_var($baseurl, FILTER_VALIDATE_URL) || substr($baseurl, -1) === '/') { - trigger_error('The URL provided is incomplete, invalid or ends with a / character!'); - - return false; + throw new InvalidBaseUrlException(); } return true; @@ -4102,13 +4235,12 @@ class Client * * @param string $site the (short) site name to check * @return bool true if (short) site name is valid, else returns false + * @throws InvalidSiteNameException when an invalid site name is provided */ protected function check_site(string $site): bool { if ($this->debug && preg_match('/\s/', $site)) { - trigger_error('The provided (short) site name may not contain any spaces'); - - return false; + throw new InvalidSiteNameException(); } return true; @@ -4209,13 +4341,13 @@ class Client * * @param string $path path for the request * @param object|array|null $payload optional, payload to pass with the request - * @return bool|string response returned by the controller API, false upon error + * @return bool|string response returned by the controller API + * @throws InvalidCurlMethodException|CurlGeneralErrorException|LoginFailedException|CurlTimeoutException */ protected function exec_curl(string $path, $payload = null) { if (!in_array($this->curl_method, self::CURL_METHODS_ALLOWED)) { - trigger_error('an invalid HTTP request type was used: ' . $this->curl_method); - return false; + throw new InvalidCurlMethodException(); } $url = $this->baseurl . $path; @@ -4272,12 +4404,17 @@ class Client /** Execute the cURL request. */ $response = curl_exec($ch); - if (curl_errno($ch)) { - trigger_error('cURL error: ' . curl_error($ch)); - } + /** Get the HTTP response code and error number. */ + $http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); + $error_number = curl_errno($ch); - /** Get the HTTP response code. */ - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($error_number) { + if (in_array($error_number, [CURLE_OPERATION_TIMEDOUT, CURLE_OPERATION_TIMEOUTED])) { + throw new CurlTimeoutException(curl_error($ch), $http_code, curl_getinfo($ch)); + } + + throw new CurlGeneralErrorException('cURL error: ' . curl_error($ch), $http_code, curl_getinfo($ch)); + } /** * An HTTP response code 401 (Unauthorized) indicates the Cookie/Token has expired, in which case @@ -4285,7 +4422,7 @@ class Client */ if ($http_code === 401) { if ($this->debug) { - error_log(__FUNCTION__ . ': needed to reconnect to UniFi controller'); + error_log(__FUNCTION__ . ': need to re-login to UniFi controller'); } if ($this->exec_retries === 0) { @@ -4304,7 +4441,7 @@ class Client curl_close($ch); - /** Login again. */ + /** Login, again. */ $this->login(); /** When the re-login was successful, execute the same cURL request again. */ @@ -4316,12 +4453,10 @@ class Client return $this->exec_curl($path, $payload); } - if ($this->debug) { - error_log(__FUNCTION__ . ': re-login failed'); - } + throw new LoginFailedException('Cookie/Token has expired and re-login failed.'); } - return false; + throw new LoginFailedException('Login failed, check the credentials.'); } if ($this->debug) { diff --git a/src/Exceptions/CurlExtensionNotLoadedException.php b/src/Exceptions/CurlExtensionNotLoadedException.php new file mode 100755 index 0000000..a77557e --- /dev/null +++ b/src/Exceptions/CurlExtensionNotLoadedException.php @@ -0,0 +1,13 @@ +_http_response_code = $http_response_code; + $this->_curl_getinfo_results = $_curl_getinfo_results; + + parent::__construct($message); + } + + /** + * Get the HTTP response code. + * + * @return mixed + */ + public function getHttpResponseCode() + { + return $this->_http_response_code; + } + + /** + * Get the cURL curl_getinfo results. + * + * @return mixed + */ + public function getCurlGetinfoResults() + { + return $this->_curl_getinfo_results; + } +} \ No newline at end of file diff --git a/src/Exceptions/CurlTimeoutException.php b/src/Exceptions/CurlTimeoutException.php new file mode 100755 index 0000000..36f57c1 --- /dev/null +++ b/src/Exceptions/CurlTimeoutException.php @@ -0,0 +1,42 @@ +_http_response_code = $http_response_code; + $this->_curl_getinfo_results = $curl_getinfo_results; + + parent::__construct($message); + } + + /** + * Get the HTTP response code. + * + * @return mixed + */ + public function getHttpResponseCode() + { + return $this->_http_response_code; + } + + /** + * Get the cURL curl_getinfo results. + * + * @return mixed + */ + public function getCurlGetinfoResults() + { + return $this->_curl_getinfo_results; + } +} \ No newline at end of file diff --git a/src/Exceptions/EmailInvalidException.php b/src/Exceptions/EmailInvalidException.php new file mode 100755 index 0000000..193d270 --- /dev/null +++ b/src/Exceptions/EmailInvalidException.php @@ -0,0 +1,13 @@ +