commit 39db30c13eb55e0c9fc9583b39b9832bc171e11c Author: jie Ago Date: Sat Nov 5 23:51:39 2022 +0800 . diff --git a/2captcha-php/.gitignore b/2captcha-php/.gitignore new file mode 100644 index 0000000..50a6ee2 --- /dev/null +++ b/2captcha-php/.gitignore @@ -0,0 +1,72 @@ +#PHP and development related +.idea +/vendor + +# Mac OS General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Linux possible garbage +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* \ No newline at end of file diff --git a/2captcha-php/README.md b/2captcha-php/README.md new file mode 100644 index 0000000..6801547 --- /dev/null +++ b/2captcha-php/README.md @@ -0,0 +1,228 @@ +# PHP Module for 2Captcha API +The easiest way to quickly integrate [2Captcha] captcha solving service into your code to automate solving of any types of captcha. + +- [Installation](#installation) + - [Composer](#composer) + - [Manual](#manual) +- [Configuration](#configuration) +- [Solve captcha](#solve-captcha) + - [Normal Captcha](#normal-captcha) + - [Text](#text-captcha) + - [ReCaptcha v2](#recaptcha-v2) + - [ReCaptcha v3](#recaptcha-v3) + - [FunCaptcha](#funcaptcha) + - [GeeTest](#geetest) + - [hCaptcha](#hcaptcha) + - [KeyCaptcha](#keycaptcha) + - [Capy](#capy) + - [Grid (ReCaptcha V2 Old Method)](#grid) + - [Canvas](#canvas) + - [ClickCaptcha](#clickcaptcha) + - [Rotate](#rotate) +- [Other methods](#other-methods) + - [send / getResult](#send--getresult) + - [balance](#balance) + - [report](#report) +- [Error handling](#error-handling) + + +## Installation +This package can be installed via composer or manually + +### Composer +``` +composer require 2captcha/2captcha +``` + +### Manual +Copy `src` directory to your project and then `require` autoloader (`src/autoloader.php`) where needed: +```php +require 'path/to/autoloader.php'; +``` + +## Configuration +`TwoCaptcha` instance can be created like this: +```php +$solver = new \TwoCaptcha\TwoCaptcha('YOUR_API_KEY'); +``` +Also there are few options that can be configured: +```php +$solver = new \TwoCaptcha\TwoCaptcha([ + 'server' => 'http://rucaptcha.com', + 'apiKey' => 'YOUR_API_KEY', + 'softId' => 123, + 'callback' => 'https://your.site/result-receiver', + 'defaultTimeout' => 120, + 'recaptchaTimeout' => 600, + 'pollingInterval' => 10, +]); +``` + +### TwoCaptcha instance options + +|Option|Default value|Description| +|---|---|---| +|softId|-|your software ID obtained after publishing in [2captcha sofware catalog]| +|callback|-|URL of your web-sever that receives the captcha recognition result. The URl should be first registered in [pingback settings] of your account| +|defaultTimeout|120|Polling timeout in seconds for all captcha types except ReCaptcha. Defines how long the module tries to get the answer from `res.php` API endpoint| +|recaptchaTimeout|600|Polling timeout for ReCaptcha in seconds. Defines how long the module tries to get the answer from `res.php` API endpoint| +|pollingInterval|10|Interval in seconds between requests to `res.php` API endpoint, setting values less than 5 seconds is not recommended| + +> **IMPORTANT:** once `callback` is defined for `TwoCaptcha` instance, all methods return only the captcha ID and DO NOT poll the API to get the result. The result will be sent to the callback URL. +To get the answer manually use [getResult method](#send--getresult) + +## Solve captcha +When you submit any image-based captcha use can provide additional options to help 2captcha workers to solve it properly. + +### Captcha options +|Option|Default Value|Description| +|---|---|---| +|numeric|0|Defines if captcha contains numeric or other symbols [see more info in the API docs][post options]| +|minLength|0|minimal answer lenght| +|maxLength|0|maximum answer length| +|phrase|0|defines if the answer contains multiple words or not| +|caseSensitive|0|defines if the answer is case sensitive| +|calc|0|defines captcha requires calculation| +|lang|-|defines the captcha language, see the [list of supported languages] | +|hintImg|-|an image with hint shown to workers with the captcha| +|hintText|-|hint or task text shown to workers with the captcha| + +Below you can find basic examples for every captcha type. Check out [examples directory] to find more examples with all available options. + +### Normal Captcha +To bypass a normal captcha (distorted text on image) use the following method. This method also can be used to recognize any text on the image. +```php +$result = $solver->normal('path/to/captcha.jpg'); +``` +### Text Captcha +This method can be used to bypass a captcha that requires to answer a question provided in clear text. +```php +$result = $solver->text('If tomorrow is Saturday, what day is today?'); +``` +### ReCaptcha v2 +Use this method to solve ReCaptcha V2 and obtain a token to bypass the protection. +```php +$result = $solver->recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', +]); +``` +### ReCaptcha v3 +This method provides ReCaptcha V3 solver and returns a token. +```php +$result = $solver->recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'version' => 'v3', +]); +``` +### FunCaptcha +FunCaptcha (Arkoselabs) solving method. Returns a token. +```php +$result = $solver->funcaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/funcaptcha', +]); +``` +### GeeTest +Method to solve GeeTest puzzle captcha. Returns a set of tokens as JSON. +```php +$result = $solver->geetest([ + 'gt' => 'f1ab2cdefa3456789012345b6c78d90e', + 'challenge' => '12345678abc90123d45678ef90123a456b', + 'url' => 'https://www.site.com/page/', +]); +``` +### hCaptcha +Use this method to solve hCaptcha challenge. Returns a token to bypass captcha. +```php +$result = $solver->hcaptcha([ + 'sitekey' => '10000000-ffff-ffff-ffff-000000000001', + 'url' => 'https://www.site.com/page/', +]); +``` +### KeyCaptcha +Token-based method to solve KeyCaptcha. +```php +$result = $solver->keycaptcha([ + 's_s_c_user_id' => 10, + 's_s_c_session_id' => '493e52c37c10c2bcdf4a00cbc9ccd1e8', + 's_s_c_web_server_sign' => '9006dc725760858e4c0715b835472f22-pz-', + 's_s_c_web_server_sign2' => '2ca3abe86d90c6142d5571db98af6714', + 'url' => 'https://www.keycaptcha.ru/demo-magnetic/', +]); +``` +### Capy +Token-based method to bypass Capy puzzle captcha. +```php +$result = $solver->capy([ + 'sitekey' => 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v', + 'url' => 'http://mysite.com/', + 'api_server' => 'https://jp.api.capy.me/', +]); +``` +### Grid +Grid method is originally called Old ReCaptcha V2 method. The method can be used to bypass any type of captcha where you can apply a grid on image and need to click specific grid boxes. Returns numbers of boxes. +```php +$result = $solver->grid('path/to/captcha.jpg'); +``` +### Canvas +Canvas method can be used when you need to draw a line around an object on image. Returns a set of points' coordinates to draw a polygon. +```php +$result = $solver->canvas('path/to/captcha.jpg'); +``` +### ClickCaptcha +ClickCaptcha method returns coordinates of points on captcha image. Can be used if you need to click on particular points on the image. +```php +$result = $solver->coordinates('path/to/captcha.jpg'); +``` +### Rotate +This method can be used to solve a captcha that asks to rotate an object. Mostly used to bypass FunCaptcha. Returns the rotation angle. +```php +$result = $solver->rotate('path/to/captcha.jpg'); +``` + +## Other methods + +### send / getResult +These methods can be used for manual captcha submission and answer polling. +```php +$id = $solver->send(['file' => 'path/to/captcha.jpg', ...]); + +sleep(20); + +$code = $solver->getResult($id); +``` +### balance +Use this method to get your account's balance +```php +$balance = $solver->balance(); +``` +### report +Use this method to report good or bad captcha answer. +```php +$solver->report($id, true); // captcha solved correctly +$solver->report($id, false); // captcha solved incorrectly +``` + +## Error handling +If case of an error captch solver throws an exception. It's important to properly handle these cases. We recommend to use `try catch` to handle exceptions. +```php +try { + $result = $solver->text('If tomorrow is Saturday, what day is today?'); +} catch (\TwoCaptcha\Exception\ValidationException $e) { + // invalid parameters passed +} catch (\TwoCaptcha\Exception\NetworkException $e) { + // network error occurred +} catch (\TwoCaptcha\Exception\ApiException $e) { + // api respond with error +} catch (\TwoCaptcha\Exception\TimeoutException $e) { + // captcha is not solved so far +} +``` +[2Captcha]: https://2captcha.com/ +[2captcha sofware catalog]: https://2captcha.com/software +[pingback settings]: https://2captcha.com/setting/pingback +[post options]: https://2captcha.com/2captcha-api#normal_post +[list of supported languages]: https://2captcha.com/2captcha-api#language +[examples directory]: /examples diff --git a/2captcha-php/composer.json b/2captcha-php/composer.json new file mode 100644 index 0000000..fefb319 --- /dev/null +++ b/2captcha-php/composer.json @@ -0,0 +1,48 @@ +{ + "name": "2captcha/2captcha", + "description": "PHP package for easy integration with 2captcha API", + "keywords": [ + "2captcha", + "captcha", + "recaptcha", + "funcaptcha", + "keycaptcha", + "geetest", + "anticaptcha", + "hcaptcha", + "recaptcha v2", + "recaptcha v3", + "bypass captcha", + "solve captcha", + "captcha solver", + "decaptcha", + "2captcha.com" + ], + "license": "MIT", + "authors": [ + { + "name": "2captcha", + "email": "info@2captcha.com", + "homepage": "https://2captcha.com/" + } + ], + "require": { + "php": ">=5.6", + "ext-curl": "*", + "ext-mbstring": "*", + "ext-fileinfo": "*" + }, + "require-dev": { + "phpunit/phpunit": "8.5.*" + }, + "autoload": { + "psr-4": { + "TwoCaptcha\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "TwoCaptcha\\Tests\\": "tests/" + } + } +} \ No newline at end of file diff --git a/2captcha-php/composer.lock b/2captcha-php/composer.lock new file mode 100644 index 0000000..b1e1ccf --- /dev/null +++ b/2captcha-php/composer.lock @@ -0,0 +1,1561 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4a41b7c8e06b03131552e056ff1d9e89", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2019-10-21T16:45:58+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2020-01-17T21:11:47+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2020-04-27T09:25:28+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "shasum": "" + }, + "require": { + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" + }, + "require-dev": { + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-02-22T12:28:44+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "shasum": "" + }, + "require": { + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-02-18T18:59:58+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.10.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "451c3cd1418cf640de218914901e51b064abb093" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5 || ^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2020-03-05T15:02:03+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "7.0.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.2", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.1.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^4.2.2", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.2.2" + }, + "suggest": { + "ext-xdebug": "^2.7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2019-11-20T13:55:58+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-06-07T04:22:29+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2019-09-17T06:23:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "8.5.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/63dda3b212a0025d380a745f91bdb4d8c985adb7", + "reference": "63dda3b212a0025d380a745f91bdb4d8c985adb7", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.9.1", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", + "php": "^7.2", + "phpspec/prophecy": "^1.8.1", + "phpunit/php-code-coverage": "^7.0.7", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.2", + "sebastian/exporter": "^3.1.1", + "sebastian/global-state": "^3.0.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", + "sebastian/version": "^2.0.1" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-05-22T13:51:52+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-02-04T06:01:07+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2019-11-20T08:46:58+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2019-09-14T09:02:43+00:00" + }, + { + "name": "sebastian/global-state", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "shasum": "" + }, + "require": { + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2019-02-01T05:30:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/type", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "shasum": "" + }, + "require": { + "php": "^7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "time": "2019-07-02T08:10:15+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:14:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-06-13T22:48:21+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^7.5.13" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2020-04-18T12:12:48+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.6", + "ext-curl": "*", + "ext-mbstring": "*", + "ext-fileinfo": "*" + }, + "platform-dev": [], + "plugin-api-version": "1.1.0" +} diff --git a/2captcha-php/examples/canvas.php b/2captcha-php/examples/canvas.php new file mode 100644 index 0000000..69b5eb2 --- /dev/null +++ b/2captcha-php/examples/canvas.php @@ -0,0 +1,18 @@ +canvas([ + 'file' => __DIR__ . '/images/canvas.jpg', + 'hintText' => 'Draw around apple', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/canvas_base64.php b/2captcha-php/examples/canvas_base64.php new file mode 100644 index 0000000..1f87cc1 --- /dev/null +++ b/2captcha-php/examples/canvas_base64.php @@ -0,0 +1,18 @@ +canvas(['base64' => $base64, 'hintText' => 'Draw around apple']); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/canvas_options.php b/2captcha-php/examples/canvas_options.php new file mode 100644 index 0000000..0fdfd6e --- /dev/null +++ b/2captcha-php/examples/canvas_options.php @@ -0,0 +1,22 @@ +canvas([ + 'file' => __DIR__ . '/images/canvas.jpg', + 'previousId' => 0, + 'canSkip' => 0, + 'lang' => 'en', + 'hintImg' => __DIR__ . '/images/canvas_hint.jpg', + 'hintText' => 'Draw around apple', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/capy.php b/2captcha-php/examples/capy.php new file mode 100644 index 0000000..3115809 --- /dev/null +++ b/2captcha-php/examples/capy.php @@ -0,0 +1,19 @@ +capy([ + 'sitekey' => 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v', + 'url' => 'https://www.mysite.com/captcha/', + 'api_server' => 'https://jp.api.capy.me/' + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/capy_options.php b/2captcha-php/examples/capy_options.php new file mode 100644 index 0000000..2fa1420 --- /dev/null +++ b/2captcha-php/examples/capy_options.php @@ -0,0 +1,23 @@ +capy([ + 'sitekey' => 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v', + 'url' => 'http://mysite.com/', + 'api_server' => 'https://jp.api.capy.me/', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/coordinates.php b/2captcha-php/examples/coordinates.php new file mode 100644 index 0000000..0dbc2c1 --- /dev/null +++ b/2captcha-php/examples/coordinates.php @@ -0,0 +1,15 @@ +coordinates(__DIR__ . '/images/grid.jpg'); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/coordinates_base64.php b/2captcha-php/examples/coordinates_base64.php new file mode 100644 index 0000000..0e6cf32 --- /dev/null +++ b/2captcha-php/examples/coordinates_base64.php @@ -0,0 +1,18 @@ +coordinates(['base64' => $base64]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/coordinates_options.php b/2captcha-php/examples/coordinates_options.php new file mode 100644 index 0000000..01c7bff --- /dev/null +++ b/2captcha-php/examples/coordinates_options.php @@ -0,0 +1,20 @@ +coordinates([ + 'file' => __DIR__ . '/images/grid_2.jpg', + 'lang' => 'en', + // 'hintImg' => __DIR__ . '/images/grid_hint.jpg' + 'hintText' => 'Select all images with an Orange', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/funcaptcha.php b/2captcha-php/examples/funcaptcha.php new file mode 100644 index 0000000..e258a6a --- /dev/null +++ b/2captcha-php/examples/funcaptcha.php @@ -0,0 +1,18 @@ +funcaptcha([ + 'sitekey' => '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'url' => 'https://mysite.com/page/with/funcaptcha', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/funcaptcha_options.php b/2captcha-php/examples/funcaptcha_options.php new file mode 100644 index 0000000..7ef4b78 --- /dev/null +++ b/2captcha-php/examples/funcaptcha_options.php @@ -0,0 +1,27 @@ +funcaptcha([ + 'sitekey' => '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'url' => 'https://mysite.com/page/with/funcaptcha', + 'surl' => 'https://client-api.arkoselabs.com', + 'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36', + 'data' => [ + 'anyKey' => 'anyStringValue', + ], + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/geetest.php b/2captcha-php/examples/geetest.php new file mode 100644 index 0000000..b63fb6c --- /dev/null +++ b/2captcha-php/examples/geetest.php @@ -0,0 +1,28 @@ +geetest([ + 'gt' => 'f2ae6cadcf7886856696502e1d55e00c', + 'apiServer' => 'api-na.geetest.com', + 'challenge' => $challenge, + 'url' => 'https://mysite.com/captcha.html', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/geetest_options.php b/2captcha-php/examples/geetest_options.php new file mode 100644 index 0000000..7394d33 --- /dev/null +++ b/2captcha-php/examples/geetest_options.php @@ -0,0 +1,30 @@ +geetest([ + 'gt' => 'f2ae6cadcf7886856696502e1d55e00c', + 'apiServer' => 'api-na.geetest.com', + 'challenge' => $challenge, + 'url' => 'https://launches.endclothing.com/distil_r_captcha.html', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/grid.php b/2captcha-php/examples/grid.php new file mode 100644 index 0000000..ffe3731 --- /dev/null +++ b/2captcha-php/examples/grid.php @@ -0,0 +1,15 @@ +grid(__DIR__ . '/images/grid.jpg'); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/grid_base64.php b/2captcha-php/examples/grid_base64.php new file mode 100644 index 0000000..6daaab8 --- /dev/null +++ b/2captcha-php/examples/grid_base64.php @@ -0,0 +1,18 @@ +grid(['base64' => $base64]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/grid_options.php b/2captcha-php/examples/grid_options.php new file mode 100644 index 0000000..c1612f3 --- /dev/null +++ b/2captcha-php/examples/grid_options.php @@ -0,0 +1,24 @@ +grid([ + 'file' => __DIR__ . '/images/grid_2.jpg', + 'rows' => 3, + 'cols' => 3, + 'previousId' => 0, + 'canSkip' => 0, + 'lang' => 'en', + // 'hintImg' => __DIR__ . '/images/grid_hint.jpg', + 'hintText' => 'Select all images with an Orange', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/hcaptcha.php b/2captcha-php/examples/hcaptcha.php new file mode 100644 index 0000000..6a25033 --- /dev/null +++ b/2captcha-php/examples/hcaptcha.php @@ -0,0 +1,18 @@ +hcaptcha([ + 'sitekey' => '10000000-ffff-ffff-ffff-000000000001', + 'url' => 'https://www.site.com/page/', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/hcaptcha_options.php b/2captcha-php/examples/hcaptcha_options.php new file mode 100644 index 0000000..b75754d --- /dev/null +++ b/2captcha-php/examples/hcaptcha_options.php @@ -0,0 +1,22 @@ +hcaptcha([ + 'sitekey' => '10000000-ffff-ffff-ffff-000000000001', + 'url' => 'https://www.site.com/page/', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/images/canvas.jpg b/2captcha-php/examples/images/canvas.jpg new file mode 100644 index 0000000..8d4a70d Binary files /dev/null and b/2captcha-php/examples/images/canvas.jpg differ diff --git a/2captcha-php/examples/images/canvas_hint.jpg b/2captcha-php/examples/images/canvas_hint.jpg new file mode 100644 index 0000000..1ecb733 Binary files /dev/null and b/2captcha-php/examples/images/canvas_hint.jpg differ diff --git a/2captcha-php/examples/images/grid.jpg b/2captcha-php/examples/images/grid.jpg new file mode 100644 index 0000000..064e4aa Binary files /dev/null and b/2captcha-php/examples/images/grid.jpg differ diff --git a/2captcha-php/examples/images/grid_2.jpg b/2captcha-php/examples/images/grid_2.jpg new file mode 100644 index 0000000..b9ce668 Binary files /dev/null and b/2captcha-php/examples/images/grid_2.jpg differ diff --git a/2captcha-php/examples/images/grid_hint.jpg b/2captcha-php/examples/images/grid_hint.jpg new file mode 100644 index 0000000..10e9686 Binary files /dev/null and b/2captcha-php/examples/images/grid_hint.jpg differ diff --git a/2captcha-php/examples/images/normal.jpg b/2captcha-php/examples/images/normal.jpg new file mode 100644 index 0000000..f1770b3 Binary files /dev/null and b/2captcha-php/examples/images/normal.jpg differ diff --git a/2captcha-php/examples/images/normal_2.jpg b/2captcha-php/examples/images/normal_2.jpg new file mode 100644 index 0000000..a1f5035 Binary files /dev/null and b/2captcha-php/examples/images/normal_2.jpg differ diff --git a/2captcha-php/examples/images/rotate.jpg b/2captcha-php/examples/images/rotate.jpg new file mode 100644 index 0000000..926612f Binary files /dev/null and b/2captcha-php/examples/images/rotate.jpg differ diff --git a/2captcha-php/examples/images/rotate_2.jpg b/2captcha-php/examples/images/rotate_2.jpg new file mode 100644 index 0000000..3e27967 Binary files /dev/null and b/2captcha-php/examples/images/rotate_2.jpg differ diff --git a/2captcha-php/examples/images/rotate_3.jpg b/2captcha-php/examples/images/rotate_3.jpg new file mode 100644 index 0000000..5f73a3b Binary files /dev/null and b/2captcha-php/examples/images/rotate_3.jpg differ diff --git a/2captcha-php/examples/keycaptcha.php b/2captcha-php/examples/keycaptcha.php new file mode 100644 index 0000000..e3a4224 --- /dev/null +++ b/2captcha-php/examples/keycaptcha.php @@ -0,0 +1,21 @@ +keycaptcha([ + 's_s_c_user_id' => 10, + 's_s_c_session_id' => '493e52c37c10c2bcdf4a00cbc9ccd1e8', + 's_s_c_web_server_sign' => '9006dc725760858e4c0715b835472f22-pz-', + 's_s_c_web_server_sign2' => '2ca3abe86d90c6142d5571db98af6714', + 'url' => 'https://www.keycaptcha.ru/demo-magnetic/', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/keycaptcha_options.php b/2captcha-php/examples/keycaptcha_options.php new file mode 100644 index 0000000..35127a7 --- /dev/null +++ b/2captcha-php/examples/keycaptcha_options.php @@ -0,0 +1,25 @@ +code = $solver->keycaptcha([ + 's_s_c_user_id' => 10, + 's_s_c_session_id' => '493e52c37c10c2bcdf4a00cbc9ccd1e8', + 's_s_c_web_server_sign' => '9006dc725760858e4c0715b835472f22-pz-', + 's_s_c_web_server_sign2' => '2ca3abe86d90c6142d5571db98af6714', + 'url' => 'https://www.keycaptcha.ru/demo-magnetic/', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/normal.php b/2captcha-php/examples/normal.php new file mode 100644 index 0000000..001e79f --- /dev/null +++ b/2captcha-php/examples/normal.php @@ -0,0 +1,15 @@ +normal(__DIR__ . '/images/normal.jpg'); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/normal_base64.php b/2captcha-php/examples/normal_base64.php new file mode 100644 index 0000000..f7eb3a0 --- /dev/null +++ b/2captcha-php/examples/normal_base64.php @@ -0,0 +1,18 @@ +normal(['base64' => $base64]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/normal_options.php b/2captcha-php/examples/normal_options.php new file mode 100644 index 0000000..b7089b4 --- /dev/null +++ b/2captcha-php/examples/normal_options.php @@ -0,0 +1,26 @@ +normal([ + 'file' => __DIR__ . '/images/normal_2.jpg', + 'numeric' => 4, + 'minLen' => 4, + 'maxLen' => 20, + 'phrase' => 1, + 'caseSensitive' => 1, + 'calc' => 0, + 'lang' => 'en', + // 'hintImg' => __DIR__ . '/images/normal_hint.jpg', + // 'hintText' => 'Type red symbols only', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/recaptcha_v2.php b/2captcha-php/examples/recaptcha_v2.php new file mode 100644 index 0000000..195d5ce --- /dev/null +++ b/2captcha-php/examples/recaptcha_v2.php @@ -0,0 +1,18 @@ +recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/recaptcha_v2_options.php b/2captcha-php/examples/recaptcha_v2_options.php new file mode 100644 index 0000000..a74e928 --- /dev/null +++ b/2captcha-php/examples/recaptcha_v2_options.php @@ -0,0 +1,27 @@ + 'YOUR_API_KEY', + 'server' => 'http://rucaptcha.com' +]); + +try { + $result = $solver->recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'invisible' => 1, + 'action' => 'verify', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/recaptcha_v3.php b/2captcha-php/examples/recaptcha_v3.php new file mode 100644 index 0000000..838d397 --- /dev/null +++ b/2captcha-php/examples/recaptcha_v3.php @@ -0,0 +1,19 @@ +recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'version' => 'v3', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/recaptcha_v3_options.php b/2captcha-php/examples/recaptcha_v3_options.php new file mode 100644 index 0000000..5897bd9 --- /dev/null +++ b/2captcha-php/examples/recaptcha_v3_options.php @@ -0,0 +1,28 @@ + 'YOUR_API_KEY', + 'server' => 'http://2captcha.com' +]); + +try { + $result = $solver->recaptcha([ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'version' => 'v3', + 'action' => 'verify', + 'score' => 0.3, + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'login:password@IP_address:PORT', + ], + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/rotate.php b/2captcha-php/examples/rotate.php new file mode 100644 index 0000000..0517527 --- /dev/null +++ b/2captcha-php/examples/rotate.php @@ -0,0 +1,15 @@ +rotate(__DIR__ . '/images/rotate.jpg'); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/rotate_many.php b/2captcha-php/examples/rotate_many.php new file mode 100644 index 0000000..2fbf29c --- /dev/null +++ b/2captcha-php/examples/rotate_many.php @@ -0,0 +1,19 @@ +rotate([ + __DIR__ . '/images/rotate.jpg', + __DIR__ . '/images/rotate_2.jpg', + __DIR__ . '/images/rotate_3.jpg', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/rotate_options.php b/2captcha-php/examples/rotate_options.php new file mode 100644 index 0000000..8eae4dc --- /dev/null +++ b/2captcha-php/examples/rotate_options.php @@ -0,0 +1,25 @@ +rotate([ + 'files' => [ + __DIR__ . '/images/rotate.jpg', + __DIR__ . '/images/rotate_2.jpg', + __DIR__ . '/images/rotate_3.jpg', + ], + 'angle' => 40, + 'lang' => 'en', + // 'hintImg' => __DIR__ . '/images/rotate_hint.jpg' + 'hintText' => 'Put the images in the correct way up', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/text.php b/2captcha-php/examples/text.php new file mode 100644 index 0000000..ae48d20 --- /dev/null +++ b/2captcha-php/examples/text.php @@ -0,0 +1,15 @@ +text('If tomorrow is Saturday, what day is today?'); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/examples/text_options.php b/2captcha-php/examples/text_options.php new file mode 100644 index 0000000..1a5cf8e --- /dev/null +++ b/2captcha-php/examples/text_options.php @@ -0,0 +1,18 @@ +text([ + 'text' => 'If tomorrow is Saturday, what day is today?', + 'lang' => 'en', + ]); +} catch (\Exception $e) { + die($e->getMessage()); +} + +die('Captcha solved: ' . $result->code); diff --git a/2captcha-php/phpunit.xml b/2captcha-php/phpunit.xml new file mode 100644 index 0000000..e871e6a --- /dev/null +++ b/2captcha-php/phpunit.xml @@ -0,0 +1,8 @@ + + + + tests + tests/autoloader.php + + + \ No newline at end of file diff --git a/2captcha-php/src/ApiClient.php b/2captcha-php/src/ApiClient.php new file mode 100644 index 0000000..dd9e76e --- /dev/null +++ b/2captcha-php/src/ApiClient.php @@ -0,0 +1,135 @@ +server = $options; + } + } + + /** + * Network client + * + * @resource + */ + private $curl; + + + /** + * Sends captcha to /in.php + * + * @param $captcha + * @param array $files + * @return bool|string + * @throws ApiException + * @throws NetworkException + */ + public function in($captcha, $files = []) + { + if (!$this->curl) $this->curl = curl_init(); + + foreach ($files as $key => $file) { + $captcha[$key] = $this->curlPrepareFile($file); + } + + curl_setopt_array($this->curl, [ + CURLOPT_URL => $this->server . '/in.php', + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_TIMEOUT => 60, + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => $captcha, + ]); + + return $this->execute(); + } + + /** + * Does request to /res.php + * + * @param $query + * @return bool|string + * @throws ApiException + * @throws NetworkException + */ + public function res($query) + { + if (!$this->curl) $this->curl = curl_init(); + + $url = $this->server . '/res.php'; + + if ($query) $url .= '?' . http_build_query($query); + + curl_setopt_array($this->curl, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_TIMEOUT => 60, + CURLOPT_POST => 0, + ]); + + return $this->execute(); + } + + /** + * Executes http request to api + * + * @return bool|string + * @throws ApiException + * @throws NetworkException + */ + private function execute() + { + $response = curl_exec($this->curl); + + if (curl_errno($this->curl)) { + throw new NetworkException(curl_error($this->curl)); + } + + if (mb_strpos($response, 'ERROR_') === 0) { + throw new ApiException($response); + } + + return $response; + } + + /** + * Different php versions have different approaches of sending files via CURL + * + * @param $file + * @return \CURLFile|string + */ + private function curlPrepareFile($file) + { + if (function_exists('curl_file_create')) { // php 5.5+ + return curl_file_create($file, mime_content_type($file), 'file'); + } else { + return '@' . realpath($file); + } + } + + /** + * Closes active CURL resource if it was created + */ + public function __destruct() + { + if ($this->curl) { + curl_close($this->curl); + } + } +} diff --git a/2captcha-php/src/Exception/ApiException.php b/2captcha-php/src/Exception/ApiException.php new file mode 100644 index 0000000..1d0ca14 --- /dev/null +++ b/2captcha-php/src/Exception/ApiException.php @@ -0,0 +1,10 @@ + $options, + ]; + } + + if (!empty($options['server'])) $this->server = $options['server']; + if (!empty($options['apiKey'])) $this->apiKey = $options['apiKey']; + if (!empty($options['softId'])) $this->softId = $options['softId']; + if (!empty($options['callback'])) $this->callback = $options['callback']; + if (!empty($options['defaultTimeout'])) $this->defaultTimeout = $options['defaultTimeout']; + if (!empty($options['recaptchaTimeout'])) $this->recaptchaTimeout = $options['recaptchaTimeout']; + if (!empty($options['pollingInterval'])) $this->pollingInterval = $options['pollingInterval']; + + $this->apiClient = new ApiClient($this->server); + } + + public function setHttpClient($apiClient) + { + $this->apiClient = $apiClient; + } + + /** + * Wrapper for solving normal captcha (image) + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function normal($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'file' => $captcha, + ]; + } + + $this->requireFileOrBase64($captcha); + + $captcha['method'] = empty($captcha['base64']) ? 'post' : 'base64'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving text captcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function text($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'text' => $captcha, + ]; + } + + $captcha['method'] = 'post'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving ReCaptcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function recaptcha($captcha) + { + $captcha['method'] = 'userrecaptcha'; + + return $this->solve($captcha, ['timeout' => $this->recaptchaTimeout]); + } + + /** + * Wrapper for solving FunCaptcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function funcaptcha($captcha) + { + $captcha['method'] = 'funcaptcha'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving GeeTest + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function geetest($captcha) + { + $captcha['method'] = 'geetest'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving hCaptcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function hcaptcha($captcha) + { + $captcha['method'] = 'hcaptcha'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving KeyCaptcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function keycaptcha($captcha) + { + $captcha['method'] = 'keycaptcha'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving Capy captcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function capy($captcha) + { + $captcha['method'] = 'capy'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving grid captcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function grid($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'file' => $captcha, + ]; + } + + $this->requireFileOrBase64($captcha); + + $captcha['method'] = empty($captcha['base64']) ? 'post' : 'base64'; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving canvas captcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function canvas($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'file' => $captcha, + ]; + } + + $this->requireFileOrBase64($captcha); + + $captcha['method'] = empty($captcha['base64']) ? 'post' : 'base64'; + $captcha['recaptcha']=1; + $captcha['canvas'] = 1; + + if ( empty($captcha['hintText']) && empty($captcha['hintImg']) ) { + throw new ValidationException('At least one of parameters: hintText or hintImg required!'); + } + + return $this->solve($captcha); + } + + /** + * Wrapper for solving coordinates captcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function coordinates($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'file' => $captcha, + ]; + } + + $this->requireFileOrBase64($captcha); + + $captcha['method'] = empty($captcha['base64']) ? 'post' : 'base64'; + $captcha['coordinatescaptcha'] = 1; + + return $this->solve($captcha); + } + + /** + * Wrapper for solving RotateCaptcha + * + * @param $captcha + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function rotate($captcha) + { + if (is_string($captcha)) { + $captcha = [ + 'file' => $captcha, + ]; + } + + if (!$this->isArrayAssoc($captcha)) { + $captcha = [ + 'files' => $captcha, + ]; + } + + if (isset($captcha['file'])) { + $captcha['files'] = [$captcha['file']]; + unset($captcha['file']); + } + + $this->prepareFilesList($captcha); + + $captcha['method'] = 'rotatecaptcha'; + + return $this->solve($captcha); + } + + /** + * Sends captcha to `/in.php` and waits for it's result. + * This helper can be used insted of manual using of `send` and `getResult` functions. + * + * @param $captcha + * @param array $waitOptions + * @return \stdClass + * @throws ApiException + * @throws NetworkException + * @throws TimeoutException + * @throws ValidationException + */ + public function solve($captcha, $waitOptions = []) + { + $result = new \stdClass(); + + $result->captchaId = $this->send($captcha); + + if ($this->lastCaptchaHasCallback) return $result; + + $result->code = $this->waitForResult($result->captchaId, $waitOptions); + + return $result; + } + + /** + * This helper waits for captcha result, and when result is ready, returns it + * + * @param $id + * @param array $waitOptions + * @return string|null + * @throws TimeoutException + */ + public function waitForResult($id, $waitOptions = []) + { + $startedAt = time(); + + $timeout = empty($waitOptions['timeout']) ? $this->defaultTimeout : $waitOptions['timeout']; + $pollingInterval = empty($waitOptions['pollingInterval']) ? $this->pollingInterval : $waitOptions['pollingInterval']; + + while (true) { + if (time() - $startedAt < $timeout) { + sleep($pollingInterval); + } else { + break; + } + + try { + $code = $this->getResult($id); + if ($code) return $code; + } catch (NetworkException $e) { + // ignore network errors + } catch (Exception $e) { + throw $e; + } + } + + throw new TimeoutException('Timeout ' . $timeout . ' seconds reached'); + } + + /** + * Sends captcha to '/in.php', and returns its `id` + * + * @param $captcha + * @return string + * @throws ApiException + * @throws NetworkException + * @throws ValidationException + */ + public function send($captcha) + { + $this->sendAttachDefaultParams($captcha); + + $files = $this->extractFiles($captcha); + + $this->mapParams($captcha, $captcha['method']); + $this->mapParams($files, $captcha['method']); + + $response = $this->apiClient->in($captcha, $files); + + if (mb_strpos($response, 'OK|') !== 0) { + throw new ApiException('Cannot recognise api response (' . $response . ')'); + } + + return mb_substr($response, 3); + } + + /** + * Returns result of captcha if it was solved or `null`, if result is not ready + * + * @param $id + * @return string|null + * @throws ApiException + * @throws NetworkException + */ + public function getResult($id) + { + $response = $this->res([ + 'action' => 'get', + 'id' => $id, + ]); + + if ($response == 'CAPCHA_NOT_READY') { + return null; + } + + if (mb_strpos($response, 'OK|') !== 0) { + throw new ApiException('Cannot recognise api response (' . $response . ')'); + } + + return mb_substr($response, 3); + } + + /** + * Gets account's balance + * + * @return float + * @throws ApiException + * @throws NetworkException + */ + public function balance() + { + $response = $this->res('getbalance'); + + return floatval($response); + } + + /** + * Reports if captcha was solved correctly (sends `reportbad` or `reportgood` to `/res.php`) + * + * @param $id + * @param $correct + * @throws ApiException + * @throws NetworkException + */ + public function report($id, $correct) + { + if ($correct) { + $this->res(['id' => $id, 'action' => 'reportgood']); + } else { + $this->res(['id' => $id, 'action' => 'reportbad']); + } + } + + /** + * Makes request to `/res.php` + * + * @param $query + * @return bool|string + * @throws ApiException + * @throws NetworkException + */ + private function res($query) + { + if (is_string($query)) { + $query = ['action' => $query]; + } + + $query['key'] = $this->apiKey; + + return $this->apiClient->res($query); + } + + /** + * Attaches default parameters (passed in constructor) to request + * + * @param $captcha + */ + private function sendAttachDefaultParams(&$captcha) + { + $captcha['key'] = $this->apiKey; + + if ($this->callback) { + if (!isset($captcha['callback'])) { + $captcha['callback'] = $this->callback; + } else if (!$captcha['callback']) { + unset($captcha['callback']); + } + } + + $this->lastCaptchaHasCallback = !empty($captcha['callback']); + + if ($this->softId and !isset($captcha['softId'])) { + $captcha['softId'] = $this->softId; + } + } + + /** + * Validates if files parameters are correct + * + * @param $captcha + * @param string $key + * @throws ValidationException + */ + private function requireFileOrBase64($captcha, $key = 'file') + { + if (!empty($captcha['base64'])) return; + + if (empty($captcha[$key])) { + throw new ValidationException('File required'); + } + + if (!file_exists($captcha[$key])) { + throw new ValidationException('File not found (' . $captcha[$key] . ')'); + } + } + + /** + * Turns `files` parameter into `file_1`, `file_2`, `file_n` parameters + * + * @param $captcha + * @throws ValidationException + */ + private function prepareFilesList(&$captcha) + { + $filesLimit = 9; + $i = 0; + + foreach ($captcha['files'] as $file) { + if (++$i > $filesLimit) { + throw new ValidationException('Too many files (max: ' . $filesLimit . ')'); + } + + if (!file_exists($file)) { + throw new ValidationException('File not found (' . $file . ')'); + } + + $captcha['file_' . $i] = $file; + } + + unset($captcha['files']); + } + + /** + * Extracts files into separate array + * + * @param $captcha + * @return array + */ + private function extractFiles(&$captcha) + { + $files = []; + + $fileKeys = ['file', 'hintImg']; + + for ($i = 1; $i < 10; $i++) { + $fileKeys[] = 'file_' . $i; + } + + foreach ($fileKeys as $key) { + if (!empty($captcha[$key]) and is_file($captcha[$key])) { + $files[$key] = $captcha[$key]; + unset($captcha[$key]); + } + } + + return $files; + } + + /** + * Turns passed parameters names into API-specific names + * + * @param $params + */ + private function mapParams(&$params, $method) + { + $map = $this->getParamsMap($method); + + foreach ($map as $new => $old) { + if (isset($params[$new])) { + $params[$old] = $params[$new]; + unset($params[$new]); + } + } + + if (isset($params['proxy'])) { + $proxy = $params['proxy']; + $params['proxy'] = $proxy['uri']; + $params['proxytype'] = $proxy['type']; + } + } + + /** + * Contains rules for `mapParams` method + * + * @param $method + * @return array + */ + private function getParamsMap($method) + { + $commonMap = [ + 'base64' => 'body', + 'caseSensitive' => 'regsense', + 'minLen' => 'min_len', + 'maxLen' => 'max_len', + 'hintText' => 'textinstructions', + 'hintImg' => 'imginstructions', + 'url' => 'pageurl', + 'score' => 'min_score', + 'text' => 'textcaptcha', + 'rows' => 'recaptcharows', + 'cols' => 'recaptchacols', + 'previousId' => 'previousID', + 'canSkip' => 'can_no_answer', + 'apiServer' => 'api_server', + 'softId' => 'soft_id', + 'callback' => 'pingback', + ]; + + $methodMap = [ + 'userrecaptcha' => [ + 'sitekey' => 'googlekey', + ], + 'funcaptcha' => [ + 'sitekey' => 'publickey', + ], + 'capy' => [ + 'sitekey' => 'captchakey', + ], + ]; + + if (isset($methodMap[$method])) { + return array_merge($commonMap, $methodMap[$method]); + } + + return $commonMap; + } + + /** + * Helper to determine if array is associative or not + * + * @param $arr + * @return bool + */ + private function isArrayAssoc($arr) + { + if (array() === $arr) return false; + return array_keys($arr) !== range(0, count($arr) - 1); + } +} diff --git a/2captcha-php/src/autoloader.php b/2captcha-php/src/autoloader.php new file mode 100644 index 0000000..6953a7e --- /dev/null +++ b/2captcha-php/src/autoloader.php @@ -0,0 +1,13 @@ +createMock(ApiClient::class); + + $data['sendParams']['key'] = $apiKey; + + $apiClient + ->expects($this->once()) + ->method('in') + ->with( + $this->equalTo($data['sendParams']), + $this->equalTo($data['sendFiles']) + ) + ->willReturn('OK|' . $captchaId); + + $apiClient + ->expects($this->once()) + ->method('res') + ->with($this->equalTo(['action' => 'get', 'id' => $captchaId, 'key' => $apiKey])) + ->willReturn('OK|' . $code); + + $solver = new TwoCaptcha([ + 'apiKey' => $apiKey, + 'pollingInterval' => 1, + ]); + + $solver->setHttpClient($apiClient); + + $result = $solver->{$this->method}($data['params']); + + $this->assertIsObject($result); + $this->assertObjectHasAttribute('code', $result); + $this->assertEquals($result->code, $code); + } + + /** + * Pass invalid file parameter to wrapper method + * in order to trigger ValidationException + */ + protected function checkIfExceptionThrownOnInvalidFile() + { + $this->expectException(ValidationException::class); + + $solver = new TwoCaptcha('API_KEY'); + + $result = $solver->{$this->method}([ + 'file' => 'non-existent-file', + ]); + } + + /** + * Pass invalid file parameter to wrapper method + * in order to trigger ValidationException + */ + protected function checkIfExceptionThrownOnTooManyFiles() + { + $this->expectException(ValidationException::class); + + $files = []; + + for ($i = 0; $i < 10; $i++) { + $files[] = __DIR__ . '/../../examples/images/rotate.jpg'; + } + + $solver = new TwoCaptcha('API_KEY'); + + $result = $solver->{$this->method}($files); + } +} diff --git a/2captcha-php/tests/CanvasTest.php b/2captcha-php/tests/CanvasTest.php new file mode 100644 index 0000000..cf5e941 --- /dev/null +++ b/2captcha-php/tests/CanvasTest.php @@ -0,0 +1,78 @@ +checkIfCorrectParamsSendAndResultReturned([ + // 'params' => $this->captchaImg, + // 'sendParams' => ['method' => 'post', 'canvas' => 1], + // 'sendFiles' => ['file' => $this->captchaImg], + // ]); + // } + + public function testSingleFileParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['file' => $this->captchaImg, 'hintText' => $this->hintText], + 'sendParams' => ['method' => 'post', 'canvas' => 1, 'recaptcha' => 1, 'textinstructions' => $this->hintText], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testBase64() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['base64' => '...', 'hintText' => $this->hintText], + 'sendParams' => ['method' => 'base64', 'canvas' => 1, 'body' => '...', 'recaptcha' => 1, 'textinstructions' => $this->hintText], + 'sendFiles' => [], + ]); + } + + public function testAllParameters() + { + $hintImg = __DIR__ . '/../examples/images/canvas_hint.jpg'; + + $params = [ + 'file' => $this->captchaImg, + 'previousId' => 0, + 'canSkip' => 0, + 'lang' => 'en', + 'hintImg' => $hintImg, + 'hintText' => $this->hintText, + ]; + + $sendParams = [ + 'method' => 'post', + 'canvas' => 1, + 'previousID' => 0, + 'can_no_answer' => 0, + 'lang' => 'en', + 'recaptcha' => 1, + 'textinstructions' => $this->hintText, + ]; + + $sendFiles = [ + 'file' => $this->captchaImg, + 'imginstructions' => $hintImg, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => $sendFiles, + ]); + } + + public function testNormalFileException() + { + $this->checkIfExceptionThrownOnInvalidFile(); + } +} diff --git a/2captcha-php/tests/CapyTest.php b/2captcha-php/tests/CapyTest.php new file mode 100644 index 0000000..129ae88 --- /dev/null +++ b/2captcha-php/tests/CapyTest.php @@ -0,0 +1,28 @@ + 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v', + 'url' => 'http://mysite.com/', + ]; + + $sendParams = [ + 'method' => 'capy', + 'captchakey' => 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v', + 'pageurl' => 'http://mysite.com/', + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/CoordinatesTest.php b/2captcha-php/tests/CoordinatesTest.php new file mode 100644 index 0000000..42fd6e5 --- /dev/null +++ b/2captcha-php/tests/CoordinatesTest.php @@ -0,0 +1,72 @@ +checkIfCorrectParamsSendAndResultReturned([ + 'params' => $this->captchaImg, + 'sendParams' => ['method' => 'post', 'coordinatescaptcha' => 1], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testSingleFileParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['file' => $this->captchaImg], + 'sendParams' => ['method' => 'post', 'coordinatescaptcha' => 1], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testBase64() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['base64' => '...'], + 'sendParams' => ['method' => 'base64', 'coordinatescaptcha' => 1, 'body' => '...'], + 'sendFiles' => [], + ]); + } + + public function testAllParameters() + { + $hintImg = __DIR__ . '/../examples/images/grid_hint.jpg'; + + $params = [ + 'file' => $this->captchaImg, + 'lang' => 'en', + 'hintImg' => $hintImg, + 'hintText' => 'Select all images with an Orange', + ]; + + $sendParams = [ + 'method' => 'post', + 'coordinatescaptcha' => 1, + 'lang' => 'en', + 'textinstructions' => 'Select all images with an Orange', + ]; + + $sendFiles = [ + 'file' => $this->captchaImg, + 'imginstructions' => $hintImg, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => $sendFiles, + ]); + } + + public function testNormalFileException() + { + $this->checkIfExceptionThrownOnInvalidFile(); + } +} diff --git a/2captcha-php/tests/FunCaptchaTest.php b/2captcha-php/tests/FunCaptchaTest.php new file mode 100644 index 0000000..363b08a --- /dev/null +++ b/2captcha-php/tests/FunCaptchaTest.php @@ -0,0 +1,38 @@ + '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'url' => 'https://mysite.com/page/with/funcaptcha', + 'surl' => 'https://client-api.arkoselabs.com', + 'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36', + 'data' => [ + 'anyKey' => 'anyStringValue', + ], + ]; + + $sendParams = [ + 'method' => 'funcaptcha', + 'publickey' => '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'pageurl' => 'https://mysite.com/page/with/funcaptcha', + 'surl' => 'https://client-api.arkoselabs.com', + 'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36', + 'data' => [ + 'anyKey' => 'anyStringValue', + ], + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/GeeTest.php b/2captcha-php/tests/GeeTest.php new file mode 100644 index 0000000..928204e --- /dev/null +++ b/2captcha-php/tests/GeeTest.php @@ -0,0 +1,32 @@ + 'f2ae6cadcf7886856696502e1d55e00c', + 'apiServer' => 'api-na.geetest.com', + 'challenge' => '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'url' => 'https://launches.endclothing.com/distil_r_captcha.html', + ]; + + $sendParams = [ + 'method' => 'geetest', + 'gt' => 'f2ae6cadcf7886856696502e1d55e00c', + 'api_server' => 'api-na.geetest.com', + 'challenge' => '69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC', + 'pageurl' => 'https://launches.endclothing.com/distil_r_captcha.html', + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/GridTest.php b/2captcha-php/tests/GridTest.php new file mode 100644 index 0000000..2a4d1ff --- /dev/null +++ b/2captcha-php/tests/GridTest.php @@ -0,0 +1,79 @@ +checkIfCorrectParamsSendAndResultReturned([ + 'params' => $this->captchaImg, + 'sendParams' => ['method' => 'post'], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testSingleFileParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['file' => $this->captchaImg], + 'sendParams' => ['method' => 'post'], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testBase64() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['base64' => '...'], + 'sendParams' => ['method' => 'base64', 'body' => '...'], + 'sendFiles' => [], + ]); + } + + public function testAllParameters() + { + $hintImg = __DIR__ . '/../examples/images/grid_hint.jpg'; + + $params = [ + 'file' => $this->captchaImg, + 'rows' => 3, + 'cols' => 3, + 'previousId' => 0, + 'canSkip' => 0, + 'lang' => 'en', + 'hintImg' => $hintImg, + 'hintText' => 'Select all images with an Orange', + ]; + + $sendParams = [ + 'method' => 'post', + 'recaptcharows' => 3, + 'recaptchacols' => 3, + 'previousID' => 0, + 'can_no_answer' => 0, + 'lang' => 'en', + 'textinstructions' => 'Select all images with an Orange', + ]; + + $sendFiles = [ + 'file' => $this->captchaImg, + 'imginstructions' => $hintImg, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => $sendFiles, + ]); + } + + public function testNormalFileException() + { + $this->checkIfExceptionThrownOnInvalidFile(); + } +} diff --git a/2captcha-php/tests/HCaptchaTest.php b/2captcha-php/tests/HCaptchaTest.php new file mode 100644 index 0000000..dcc49c3 --- /dev/null +++ b/2captcha-php/tests/HCaptchaTest.php @@ -0,0 +1,28 @@ + 'f1ab2cdefa3456789012345b6c78d90e', + 'url' => 'https://www.site.com/page/', + ]; + + $sendParams = [ + 'method' => 'hcaptcha', + 'sitekey' => 'f1ab2cdefa3456789012345b6c78d90e', + 'pageurl' => 'https://www.site.com/page/', + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/KeyCaptchaTest.php b/2captcha-php/tests/KeyCaptchaTest.php new file mode 100644 index 0000000..c1b57c2 --- /dev/null +++ b/2captcha-php/tests/KeyCaptchaTest.php @@ -0,0 +1,34 @@ + 10, + 's_s_c_session_id' => '493e52c37c10c2bcdf4a00cbc9ccd1e8', + 's_s_c_web_server_sign' => '9006dc725760858e4c0715b835472f22-pz-', + 's_s_c_web_server_sign2' => '2ca3abe86d90c6142d5571db98af6714', + 'url' => 'https://www.keycaptcha.ru/demo-magnetic/', + ]; + + $sendParams = [ + 'method' => 'keycaptcha', + 's_s_c_user_id' => 10, + 's_s_c_session_id' => '493e52c37c10c2bcdf4a00cbc9ccd1e8', + 's_s_c_web_server_sign' => '9006dc725760858e4c0715b835472f22-pz-', + 's_s_c_web_server_sign2' => '2ca3abe86d90c6142d5571db98af6714', + 'pageurl' => 'https://www.keycaptcha.ru/demo-magnetic/', + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/NormalTest.php b/2captcha-php/tests/NormalTest.php new file mode 100644 index 0000000..e04c9f3 --- /dev/null +++ b/2captcha-php/tests/NormalTest.php @@ -0,0 +1,83 @@ +checkIfCorrectParamsSendAndResultReturned([ + 'params' => $this->captchaImg, + 'sendParams' => ['method' => 'post'], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testSingleFileParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['file' => $this->captchaImg], + 'sendParams' => ['method' => 'post'], + 'sendFiles' => ['file' => $this->captchaImg], + ]); + } + + public function testBase64() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['base64' => '...'], + 'sendParams' => ['method' => 'base64', 'body' => '...'], + 'sendFiles' => [], + ]); + } + + public function testAllParameters() + { + $hintImg = __DIR__ . '/../examples/images/grid_hint.jpg'; + + $params = [ + 'file' => $this->captchaImg, + 'numeric' => 4, + 'minLen' => 4, + 'maxLen' => 20, + 'phrase' => 1, + 'caseSensitive' => 1, + 'calc' => 0, + 'lang' => 'en', + 'hintImg' => $hintImg, + 'hintText' => 'Type red symbols only', + ]; + + $sendParams = [ + 'method' => 'post', + 'numeric' => 4, + 'min_len' => 4, + 'max_len' => 20, + 'phrase' => 1, + 'regsense' => 1, + 'calc' => 0, + 'lang' => 'en', + 'textinstructions' => 'Type red symbols only', + ]; + + $sendFiles = [ + 'file' => $this->captchaImg, + 'imginstructions' => $hintImg, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => $sendFiles, + ]); + } + + public function testNormalFileException() + { + $this->checkIfExceptionThrownOnInvalidFile(); + } +} diff --git a/2captcha-php/tests/ReCaptchaTest.php b/2captcha-php/tests/ReCaptchaTest.php new file mode 100644 index 0000000..d703e3b --- /dev/null +++ b/2captcha-php/tests/ReCaptchaTest.php @@ -0,0 +1,64 @@ + '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'invisible' => 1, + 'action' => 'verify', + 'proxy' => [ + 'type' => 'HTTPS', + 'uri' => 'username:str0ngP@$$W0rd@1.2.3.4:4321', + ] + ]; + + $sendParams = [ + 'method' => 'userrecaptcha', + 'googlekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'pageurl' => 'https://mysite.com/page/with/recaptcha', + 'invisible' => 1, + 'action' => 'verify', + 'proxy' => 'username:str0ngP@$$W0rd@1.2.3.4:4321', + 'proxytype' => 'HTTPS' + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } + + public function testV3() + { + $params = [ + 'sitekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'url' => 'https://mysite.com/page/with/recaptcha', + 'version' => 'v3', + 'action' => 'verify', + 'score' => 0.3, + ]; + + $sendParams = [ + 'method' => 'userrecaptcha', + 'googlekey' => '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-', + 'pageurl' => 'https://mysite.com/page/with/recaptcha', + 'version' => 'v3', + 'action' => 'verify', + 'min_score' => 0.3, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/RotateTest.php b/2captcha-php/tests/RotateTest.php new file mode 100644 index 0000000..0aae47b --- /dev/null +++ b/2captcha-php/tests/RotateTest.php @@ -0,0 +1,113 @@ +checkIfCorrectParamsSendAndResultReturned([ + 'params' => $this->img, + 'sendParams' => ['method' => 'rotatecaptcha'], + 'sendFiles' => ['file_1' => $this->img], + ]); + } + + public function testSingleFileParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['file' => $this->img], + 'sendParams' => ['method' => 'rotatecaptcha'], + 'sendFiles' => ['file_1' => $this->img], + ]); + } + + public function testFilesList() + { + $files = [ + $this->img, + $this->img2, + $this->img3, + ]; + + $sendFiles = [ + 'file_1' => $this->img, + 'file_2' => $this->img2, + 'file_3' => $this->img3, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $files, + 'sendParams' => ['method' => 'rotatecaptcha'], + 'sendFiles' => $sendFiles, + ]); + } + + public function testFilesListParameter() + { + $files = [ + $this->img, + $this->img2, + $this->img3, + ]; + + $sendFiles = [ + 'file_1' => $this->img, + 'file_2' => $this->img2, + 'file_3' => $this->img3, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['files' => $files], + 'sendParams' => ['method' => 'rotatecaptcha'], + 'sendFiles' => $sendFiles, + ]); + } + + public function testAllParameters() + { + $hintImg = __DIR__ . '/../examples/images/grid_hint.jpg'; + + $params = [ + 'file' => $this->img, + 'angle' => 40, + 'lang' => 'en', + 'hintImg' => $hintImg, + 'hintText' => 'Put the images in the correct way up', + ]; + + $sendParams = [ + 'method' => 'rotatecaptcha', + 'angle' => 40, + 'lang' => 'en', + 'textinstructions' => 'Put the images in the correct way up', + ]; + + $sendFiles = [ + 'file_1' => $this->img, + 'imginstructions' => $hintImg, + ]; + + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => $params, + 'sendParams' => $sendParams, + 'sendFiles' => $sendFiles, + ]); + } + + public function testInvalidFileException() + { + $this->checkIfExceptionThrownOnInvalidFile(); + } + + public function testTooManyFilesException() + { + $this->checkIfExceptionThrownOnTooManyFiles(); + } +} diff --git a/2captcha-php/tests/TextTest.php b/2captcha-php/tests/TextTest.php new file mode 100644 index 0000000..e6c19fd --- /dev/null +++ b/2captcha-php/tests/TextTest.php @@ -0,0 +1,35 @@ +checkIfCorrectParamsSendAndResultReturned([ + 'params' => 'Today is monday?', + 'sendParams' => ['method' => 'post', 'textcaptcha' => 'Today is monday?'], + 'sendFiles' => [], + ]); + } + + public function testTextParameter() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['text' => 'Today is monday?'], + 'sendParams' => ['method' => 'post', 'textcaptcha' => 'Today is monday?'], + 'sendFiles' => [], + ]); + } + + public function testAllParameters() + { + $this->checkIfCorrectParamsSendAndResultReturned([ + 'params' => ['text' => 'Today is monday?', 'lang' => 'en'], + 'sendParams' => ['method' => 'post', 'textcaptcha' => 'Today is monday?', 'lang' => 'en'], + 'sendFiles' => [], + ]); + } +} diff --git a/2captcha-php/tests/autoloader.php b/2captcha-php/tests/autoloader.php new file mode 100644 index 0000000..05a80c5 --- /dev/null +++ b/2captcha-php/tests/autoloader.php @@ -0,0 +1,14 @@ + + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3201b23 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# MCBBSAutoSignIn +MCBBS自动签到脚本 + +----------------------------- +代码使用2Captcha服务来过极验验证 + +[网址](2captcha.com) +``` +// 2Captcha的api_key,用于过极验验证 +$api_key = ""; +// mcbbs的cookie +$cookie = ""; +``` +api_key和cookie自行填入 diff --git a/Sign.php b/Sign.php new file mode 100644 index 0000000..fe03555 --- /dev/null +++ b/Sign.php @@ -0,0 +1,199 @@ +challenge; + // $challenge = explode(";", $resp)[0]; + + // Then we are ready to make a call to 2captcha API + $flag = 0; + $error = null; + + try { + $result = $solver->geetest([ + 'gt' => 'c4c41e397ee921e9862d259da2a031c4', + 'apiServer' => 'api.geetest.com', + 'challenge' => $challenge, + 'url' => 'https://www.mcbbs.net/plugin.php?id=dc_signin', + ]); + } catch (\Exception $e) { + print_r($e->getMessage()); + $error = $e->getMessage(); + $flag ++; + } + }while($flag < 10 && $error == "ERROR_CAPTCHA_UNSOLVABLE"); + + + $data = json_decode($result->code,true); + return $data; + +} + +function getFormHash(string $cookie) { + $data = get("https://www.mcbbs.net/home.php?mod=spacecp&inajax=1",$cookie); + preg_match("//",$data,$match); + if(!isset($match[1])){ + return false; + } + return $match[1]; +} + +function get(string $url,string $cookie,array $header = null){ + $c = curl_init(); + curl_setopt($c,CURLOPT_URL,$url); + curl_setopt($c,CURLOPT_COOKIE,$cookie); + curl_setopt($c, CURLOPT_HEADER, 0); + curl_setopt($c,CURLOPT_SSL_VERIFYPEER,false); + curl_setopt($c,CURLOPT_RETURNTRANSFER,true); + curl_setopt($c,CURLOPT_HTTPHEADER, [ + "Referer: https://www.mcbbs.net/plugin.php?id=dc_signin", + "Connection: closed", + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" + ]); + if(!is_null($header)) { + curl_setopt($c,CURLOPT_HTTPHEADER, $header); + } + $x = curl_exec($c); + curl_close($c); + return $x; +} +function post(string $url,string $cookie, array $data,array $header = null) { + $c = curl_init(); + curl_setopt($c,CURLOPT_URL,$url); + curl_setopt($c,CURLOPT_COOKIE,$cookie); + curl_setopt($c, CURLOPT_HEADER, 0); + curl_setopt($c,CURLOPT_SSL_VERIFYPEER,false); + curl_setopt($c,CURLOPT_RETURNTRANSFER,true); + curl_setopt($c, CURLOPT_POST, 1); + curl_setopt($c,CURLOPT_HTTPHEADER, [ + "Referer: https://www.mcbbs.net/plugin.php?id=dc_signin", + "Connection: closed", + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" + ]); + if(!is_null($header)) { + curl_setopt($c,CURLOPT_HTTPHEADER, $header); + } + curl_setopt($c, CURLOPT_POSTFIELDS, $data); + $x = curl_exec($c); + curl_close($c); + return $x; +} + +function checkSignIn (string $cookie) { + $signUrl = "https://www.mcbbs.net/plugin.php?id=dc_signin:sign&inajax=1"; + $check = get($signUrl, $cookie); + if(!$check || preg_match("/您今日已经签过到/",$check)) { + return true; + } + return false; +} + +function signIn(string $cookie, string $emote,string $content,array $chal) { + $signUrl = "https://www.mcbbs.net/plugin.php?id=dc_signin:sign&inajax=1"; + $hash = getFormHash($cookie); + if(!$hash){return false;} + $result = post($signUrl, $cookie, [ + "formhash" => $hash, + "signsubmit" => "yes", + "handlekey" => "signin", + "emotid" => $emote, + "referer" => "https://www.mcbbs.net/plugin.php?id=dc_signin", + "content" => $content, + "geetest_challenge" => $chal['geetest_challenge'], + "geetest_validate" => $chal['geetest_validate'], + "geetest_seccode" => $chal['geetest_seccode'] + ]); + if(!preg_match("/签到成功/",$result)){ + echo $result.PHP_EOL; + $res1[] = false; + $res1[] = $result; + return $res1; + } + $res2[] = true; + $res2[] = $result; + return $res2; +} + +function sc_send( $text , $desp = '' , $key = '[SENDKEY]' ) +{ + $data = array( + 'text' => $text, + 'desp' => $desp + ); + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://sctapi.ftqq.com/'.$key.'.send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); + + $headers = array(); + $headers[] = 'Content-type: application/x-www-form-urlencoded'; + + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $resp = curl_exec($ch); + if (curl_errno($ch)) { + echo 'Error:' . curl_error($ch); + } + curl_close($ch); + + return $resp; +}