diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8b16fa4..a8833dc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,6 +10,8 @@ jobs: - '7.0' - '7.4' - '8.2' + - '8.3' + - '8.4' name: PHP ${{ matrix.php-version }} sample steps: - uses: actions/checkout@v3 @@ -20,4 +22,4 @@ jobs: - run: composer install - env: DETECTLANGUAGE_API_KEY: ${{ secrets.DETECTLANGUAGE_API_KEY }} - run: bin/phpunit + run: composer test-silent diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a325db2 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,13 @@ +name: Release to Packagist +on: + release: + types: [published] +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Dispatch packagist update + run: | + curl -X POST https://packagist.org/api/update-package?username=${{ secrets.PACKAGIST_USERNAME }}&apiToken=${{ secrets.PACKAGIST_API_TOKEN }} \ + -d '{"repository":{"url":"https://github.com/detectlanguage/detectlanguage-php"}}' \ + -H "Content-Type: application/json" diff --git a/.gitignore b/.gitignore index bd4126e..b39dec0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env.local .DS_Store .AppleDouble .LSOverride @@ -6,6 +7,7 @@ Icon .Spotlight-V100 .Trashes *.sublime-* +.phpunit.result.cache vendor composer.lock bin diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..45a389c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## v3.0.0 + +### Added +- `detectBatch` method for batch detections + +### Changed +- Switched to v3 API which uses updated language detection model +- ⚠️ `detect` method result fields are `language` and `score` + +### Deprecated +- `simpleDetect()` - Use `detectCode()` instead. +- Calling `detect()` with array argument. Use `detectBatch()` instead. + +### Removed +- Secure mode configuration. HTTPS is always used. diff --git a/README.md b/README.md index 1abecb3..dc0eb60 100644 --- a/README.md +++ b/README.md @@ -28,26 +28,27 @@ Create or add the following to composer.json in your project root: } ``` +## Upgrading + +When upgrading please check [changelog](CHANGELOG.md) for breaking changes. + ## Usage ### Configuration Before using Detect Language API client you have to setup your personal API key. -You can get it by signing up at http://detectlanguage.com +You can get it by signing up at https://detectlanguage.com ```php use \DetectLanguage\DetectLanguage; DetectLanguage::setApiKey("YOUR API KEY"); - -// Enable secure mode if passing sensitive information -// DetectLanguage::setSecure(true); ``` -### Language detection +### Detect language ```php -$results = DetectLanguage::detect("Buenos dias señor"); +$results = DetectLanguage::detect("Dolce far niente"); ``` #### Results @@ -57,36 +58,35 @@ Array ( [0] => stdClass Object ( - [language] => es - [isReliable] => 1 - [confidence] => 10.24 + [language] => it + [score] => 0.5074 ) ) ``` -### Simple language detection +### Detect single code -If you need just a language code you can use `simpleDetect`. It returns just the language code. +If you need just a language code you can use `detectCode`. It returns just the language code. ```php -$languageCode = DetectLanguage::simpleDetect("Buenos dias señor"); +$languageCode = DetectLanguage::detectCode("Dolce far niente"); ``` #### Result ```php -"es" +"it" ``` ### Batch detection It is possible to detect language of several texts with one request. This method is faster than doing one request per text. -To use batch detection just pass array of texts to `detect` method. +To use batch detection just pass array of texts to `detectBatch` method. ```php -$results = DetectLanguage::detect(array("Buenos dias señor", "Hello world")); +$results = DetectLanguage::detectBatch(array("Dolce far niente", "Hello world")); ``` #### Results @@ -100,9 +100,8 @@ Array ( [0] => stdClass Object ( - [language] => es - [isReliable] => 1 - [confidence] => 10.24 + [language] => it + [score] => 0.5074 ) ) @@ -112,8 +111,7 @@ Array [0] => stdClass Object ( [language] => en - [isReliable] => 1 - [confidence] => 11.94 + [score] => 0.9098 ) ) @@ -143,6 +141,31 @@ stdClass Object ) ``` +### Get list of supported languages + +```php +$results = DetectLanguage::getLanguages(); +``` + +#### Result + +```php +Array +( + [0] => stdClass Object + ( + [code] => aa + [name] => Afar + ) + + [1] => stdClass Object + ( + [code] => ab + [name] => Abkhazian + ) + ... +``` + ## License Detect Language API Client is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. diff --git a/composer.json b/composer.json index d8168da..6b1c902 100644 --- a/composer.json +++ b/composer.json @@ -24,5 +24,9 @@ "psr-0": { "DetectLanguage": "lib/" } + }, + "scripts": { + "test": "php -d memory_limit=512M vendor/phpunit/phpunit/phpunit", + "test-silent": "php -d memory_limit=512M -d error_reporting='E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED' vendor/phpunit/phpunit/phpunit" } } diff --git a/lib/DetectLanguage/Client.php b/lib/DetectLanguage/Client.php index d78e1d4..ed8a912 100644 --- a/lib/DetectLanguage/Client.php +++ b/lib/DetectLanguage/Client.php @@ -28,20 +28,27 @@ class Client /** * Perform a request * - * @param string $method Method name - * @param array $params The parameters to use for the POST body + * @param string $method HTTP method name (GET, POST, etc.) + * @param string $path API endpoint path + * @param array|null $payload Request payload data * - * @return object + * @return array Response data + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails */ - public static function request($method, $params = null) + public static function request($method, $path, $payload = null) { - $url = self::getUrl($method); + $url = self::getUrl($path); + + if ($payload !== null) + $body = json_encode($payload); + else + $body = null; - $request_method = self::getRequestMethodName(); - $response_body = self::$request_method($url, $params); + $engine_method = self::getEngineMethodName(); + $response_body = self::$engine_method($method,$url, $body); $response = json_decode($response_body); - if (!is_object($response)) + if (!is_object($response) && !is_array($response)) throw new Error("Invalid server response: $response_body"); if (isset($response->error)) @@ -50,12 +57,7 @@ public static function request($method, $params = null) return $response; } - /** - * Get request method name. - * - * @return string - */ - protected static function getRequestMethodName() + protected static function getEngineMethodName() { $request_engine = self::$requestEngine; @@ -79,23 +81,27 @@ protected static function getRequestMethodName() /** * Perform request using native PHP streams * + * @param string $method HTTP method name * @param string $url Request URL - * @param array $params The parameters to use for the POST body + * @param string|null $body Request body * * @return string Response body */ - protected static function requestStream($url, $params) + protected static function requestStream($method, $url, $body) { $opts = array('http' => array( - 'method' => 'POST', + 'method' => $method, 'header' => implode("\n", self::getHeaders()), - 'content' => json_encode($params), 'timeout' => self::$requestTimeout, 'ignore_errors' => true, ) ); + if ($body !== null) { + $opts['http']['content'] = $body; + } + $context = stream_context_create($opts); return file_get_contents($url, false, $context); @@ -104,25 +110,31 @@ protected static function requestStream($url, $params) /** * Perform request using CURL extension. * + * @param string $method HTTP method name * @param string $url Request URL - * @param array $params The parameters to use for the POST body + * @param string|null $body Request body * * @return string Response body + * @throws \DetectLanguage\Error When CURL request fails, times out, or connection fails */ - protected static function requestCurl($url, $params) + protected static function requestCurl($method, $url, $body) { $ch = curl_init(); $options = array( + CURLOPT_CUSTOMREQUEST => $method, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => self::getHeaders(), - CURLOPT_POSTFIELDS => json_encode($params), CURLOPT_CONNECTTIMEOUT => self::$connectTimeout, CURLOPT_TIMEOUT => self::$requestTimeout, CURLOPT_USERAGENT => self::getUserAgent(), CURLOPT_RETURNTRANSFER => true ); + if ($body !== null) { + $options[CURLOPT_POSTFIELDS] = $body; + } + curl_setopt_array($ch, $options); $result = curl_exec($ch); @@ -142,27 +154,17 @@ protected static function requestCurl($url, $params) * Build URL for given method * * @param string $method Method name - * @return string + * @return string Complete API URL */ protected static function getUrl($method) { - return self::getProtocol() . '://' . DetectLanguage::$host . '/' . DetectLanguage::$apiVersion . '/' . $method; - } - - /** - * Get protocol for request. - * - * @return string 'https' or 'http' - */ - protected static function getProtocol() - { - return DetectLanguage::$secure ? 'https' : 'http'; + return 'https://' . DetectLanguage::$host . '/' . DetectLanguage::$apiVersion . '/' . $method; } /** * Build request headers. * - * @return array + * @return array Array of HTTP headers */ protected static function getHeaders() { @@ -177,7 +179,7 @@ protected static function getHeaders() /** * Get User-Agent for the request. * - * @return string + * @return string User-Agent string */ protected static function getUserAgent() { diff --git a/lib/DetectLanguage/DetectLanguage.php b/lib/DetectLanguage/DetectLanguage.php index cfb4ae5..55c9189 100644 --- a/lib/DetectLanguage/DetectLanguage.php +++ b/lib/DetectLanguage/DetectLanguage.php @@ -31,55 +31,41 @@ class DetectLanguage * @static * @var string */ - public static $apiVersion = '0.2'; - - /** - * Enable secure mode (SSL). - * - * @static - * @var boolean - */ - public static $secure; + public static $apiVersion = 'v3'; /** * API Client Version. */ - const VERSION = '2.2.1'; + const VERSION = '3.0.0'; /** * Set API key * * @static - * @param string $apiKey + * @param string $apiKey The API key for authentication + * @return void */ public static function setApiKey($apiKey) { self::$apiKey = $apiKey; } - /** - * Set secure mode - * - * @static - * @param boolean $secure - */ - public static function setSecure($secure) - { - self::$secure = $secure; - } - /** * Detect text language. * * @static * @param string $text The text for language detection - * @return array detected languages information + * @return array Detected languages information + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails */ public static function detect($text) { - $result = Client::request('detect', array('q' => $text)); + if (is_array($text)) { + trigger_error('detect method does not accept arrays, use detectBatch instead', E_USER_DEPRECATED); + return self::detectBatch($text); + } - return $result->data->detections; + return Client::request('POST', 'detect', array('q' => $text)); } /** @@ -87,9 +73,10 @@ public static function detect($text) * * @static * @param string $text The text for language detection - * @return string|null detected language code + * @return string|null Detected language code + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails */ - public static function simpleDetect($text) + public static function detectCode($text) { $detections = self::detect($text); @@ -99,8 +86,54 @@ public static function simpleDetect($text) return null; } + /** + * Detect text language in batch. + * + * @static + * @param array $texts The texts for language detection + * @return array Detected languages information + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails + */ + public static function detectBatch($texts) + { + return Client::request('POST', 'detect-batch', array('q' => $texts)); + } + + /** + * Get account status. + * + * @static + * @return array account status information + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails + */ public static function getStatus() { - return Client::request('user/status'); + return Client::request('GET', 'account/status'); + } + + /** + * Get supported languages. + * + * @static + * @return array Supported languages information + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails + */ + public static function getLanguages() + { + return Client::request('GET', 'languages'); + } + + // DEPRECATED METHODS + + /** + * @deprecated use self::detectCode instead + * @param string $text The text for language detection + * @return string|null Detected language code + * @throws \DetectLanguage\Error When API request fails, invalid response received, or authentication fails + */ + public static function simpleDetect($text) + { + trigger_error('simpleDetect method is deprecated, use detectCode instead', E_USER_DEPRECATED); + return self::detectCode($text); } } diff --git a/lib/DetectLanguage/Error.php b/lib/DetectLanguage/Error.php index 731f46c..c905e37 100644 --- a/lib/DetectLanguage/Error.php +++ b/lib/DetectLanguage/Error.php @@ -2,7 +2,9 @@ namespace DetectLanguage; -// backwards compatibility +/** + * @deprecated Use Error class instead + */ class DetectLanguageError extends \Exception { } diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..3560a5f --- /dev/null +++ b/mise.toml @@ -0,0 +1,9 @@ +[tools] + +[env] +_.file = ".env.local" + +[tasks] +test = "composer test" +test-silent = "composer test-silent" + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3c2c692..d83512b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,15 +1,13 @@ - - - - - ./tests/DetectLanguage/ - - - - - - ./lib/DetectLanguage/ - - + + + + ./lib/DetectLanguage/ + + + + + ./tests/DetectLanguage/ + + diff --git a/tests/DetectLanguage/DetectLanguageTest.php b/tests/DetectLanguage/DetectLanguageTest.php index 4174d2a..c2a877a 100644 --- a/tests/DetectLanguage/DetectLanguageTest.php +++ b/tests/DetectLanguage/DetectLanguageTest.php @@ -12,7 +12,6 @@ public function set_up() parent::set_up(); DetectLanguage::$apiKey = getenv('DETECTLANGUAGE_API_KEY'); - DetectLanguage::$apiVersion = '0.2'; } public function testConstructor() @@ -24,7 +23,7 @@ public function testConstructor() 'Expect the API key to be set.'); } - public function testDetection() + public function testDetect() { $result = DetectLanguage::detect('Hello world'); @@ -37,14 +36,29 @@ public function testDetection() 'To detect Lithuanian language.'); } - public function testSimpleDetection() + public function testDetectWithArray() { - $result = DetectLanguage::simpleDetect('Hello world'); + $result = DetectLanguage::detect(array('Hello world')); + + $this->assertEquals('en', $result[0][0]->language, + 'To detect English language.'); + } + + public function testDetectCode() + { + $result = DetectLanguage::detectCode('Hello world'); $this->assertEquals('en', $result, 'To detect English language.'); } + public function testSimpleDetect() + { + $result = DetectLanguage::simpleDetect('Hello world'); + $this->assertEquals('en', $result, + 'To detect English language.'); + } + public function testCurlRequest() { $this->setRequestEngine('curl'); @@ -57,20 +71,13 @@ public function testStreamRequest() $this->__request(); } - public function testSecureRequest() - { - DetectLanguage::setSecure(true); - $this->__request(); - DetectLanguage::setSecure(false); - } - public function testInvalidApiKey() { $this->expectException('\DetectLanguage\Error'); DetectLanguage::setApiKey('invalid'); - $result = DetectLanguage::simpleDetect('Hello world'); + $result = DetectLanguage::detectCode('Hello world'); } public function testErrorBackwardsCompatibility() @@ -79,15 +86,14 @@ public function testErrorBackwardsCompatibility() DetectLanguage::setApiKey('invalid'); - $result = DetectLanguage::simpleDetect('Hello world'); + $result = DetectLanguage::detectCode('Hello world'); } public function testInvalidResponse() { $this->expectException('\DetectLanguage\Error'); - DetectLanguage::$apiVersion = '0.0'; - DetectLanguage::simpleDetect('Hello world'); + DetectLanguage::detect(''); } public function testBatchDetectionWithCurl() @@ -120,6 +126,13 @@ public function testGetStatus() $this->assertEquals($response->status, 'ACTIVE'); } + public function testGetLanguages() + { + $response = DetectLanguage::getLanguages(); + $this->assertIsArray($response); + $this->assertIsString($response[0]->code); + } + private function setRequestEngine($engine) { \DetectLanguage\Client::$requestEngine = $engine; @@ -127,14 +140,14 @@ private function setRequestEngine($engine) private function __request() { - $result = DetectLanguage::simpleDetect('Hello world'); + $result = DetectLanguage::detectCode('Hello world'); $this->assertEquals('en', $result, 'To detect English language.'); } private function __batchDetection() { - $result = DetectLanguage::detect(array('Hello world', 'Jau saulelė vėl atkopdama budino svietą')); + $result = DetectLanguage::detectBatch(array('Hello world', 'Jau saulelė vėl atkopdama budino svietą')); $this->assertEquals('en', $result[0][0]->language, 'To detect English language.'); @@ -170,7 +183,7 @@ private function __batchDetectionOrder() 'hello', ); - $result = DetectLanguage::detect($request); + $result = DetectLanguage::detectBatch($request); foreach ($request as $i => $phrase) { $language = $phrase == 'hello' ? 'en' : 'ru';