├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── readme_bootstrap_sample.png ├── src ├── MathCaptcha.php ├── MathCaptchaServiceProvider.php ├── config │ └── math-captcha.php └── resources │ └── lang │ ├── de │ └── math-captcha.php │ ├── en │ └── math-captcha.php │ └── sv │ └── math-captcha.php └── tests ├── .gitkeep └── MathCaptchaTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2018 Matthias Lill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Very simple math captcha for Laravel5 2 | 3 | A simple math question (`+`,`-`,`*`) to validate user input. 4 | 5 | ## Installation 6 | 7 | ``` 8 | composer require elic-dev/laravel-math-captcha 9 | ``` 10 | 11 | ### Setup Laravel > 5.5 12 | 13 | This package supports Laravel Package Auto-Discovery. 14 | 15 | ### Setup Laravel <= 5.4 16 | 17 | You can add the ServiceProvider to the providers array in `app/config/app.php`. 18 | 19 | ``` 20 | ElicDev\MathCaptcha\MathCaptchaServiceProvider::class, 21 | ``` 22 | 23 | 24 | ## Usage 25 | 26 | This package only returns the question and the input. You have to position it within your labels and form classes. 27 | 28 | ```php 29 | {{ app('mathcaptcha')->label() }} 30 | {!! app('mathcaptcha')->input() !!} 31 | ``` 32 | 33 | Display it wihtin Bootstrap as example: 34 | 35 | ``` 36 |
37 | 38 | {!! app('mathcaptcha')->input(['class' => 'form-control', 'id' => 'mathgroup']) !!} 39 | @if ($errors->has('mathcaptcha')) 40 | 41 | {{ $errors->first('mathcaptcha') }} 42 | 43 | @endif 44 |
45 | ``` 46 | 47 | Looks like 48 | 49 | ![MathCaptcha Bootstrap](https://raw.githubusercontent.com/elic-dev/laravel-math-captcha/master/readme_bootstrap_sample.png) 50 | 51 | 52 | #### Validation 53 | 54 | Add `'mathcaptcha' => 'required|mathcaptcha'` to rules array. 55 | 56 | 57 | ```php 58 | $this->validate($request, [ 59 | 'mathcaptcha' => 'required|mathcaptcha', 60 | ]); 61 | 62 | ``` 63 | 64 | Add corresponding translation string to your `lang/validation.php` files. 65 | 66 | #### Reset 67 | 68 | This package does not generate a new math question for each request. Once the 69 | form has been submited without validation erros you can reset the library to force 70 | generate a new question. 71 | 72 | ```php 73 | app('mathcaptcha')->reset(); 74 | ``` 75 | 76 | ## Configuration 77 | 78 | ### Operands, Min, Max 79 | 80 | You can adjust the available operands (`+`,`-`,`*`) and minimum or maximum randum 81 | values used. Some users might stuggle with more complex math operations. 82 | 83 | 84 | ``` 85 | php artisan vendor:publish --provider="ElicDev\MathCaptcha\MathCaptchaServiceProvider" --tag=config 86 | ``` 87 | 88 | ### Display as text 89 | 90 | It is possible to show the math question as text (e.g. "Four plus Five"). You can adjust a setting in the config file. This requires translations and a language files. A few languages are provided with this package. 91 | 92 | 93 | ``` 94 | php artisan vendor:publish --provider="ElicDev\MathCaptcha\MathCaptchaServiceProvider" --tag=lang 95 | ``` 96 | 97 | 98 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elic-dev/laravel-math-captcha", 3 | "description": "A simple math captcha for Laravel form validation.", 4 | "keywords": ["captcha", "math-captcha", "math", "laravel", "laravel5"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Matthias Lill", 9 | "email": "m.lill@gmx.de" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "ElicDev\\MathCaptcha\\": "src/" 15 | } 16 | }, 17 | "extra": { 18 | "laravel": { 19 | "providers": [ 20 | "ElicDev\\MathCaptcha\\MathCaptchaServiceProvider" 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /readme_bootstrap_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elic-dev/laravel-math-captcha/8c79f79297126b43e388ac8f0ab0f7da4bb0fb5b/readme_bootstrap_sample.png -------------------------------------------------------------------------------- /src/MathCaptcha.php: -------------------------------------------------------------------------------- 1 | session = $session; 21 | } 22 | 23 | /** 24 | * Returns the math question as string. The second operand is always a larger 25 | * number then the first one. So it's on first position because we don't want 26 | * any negative results. 27 | * 28 | * @return string 29 | */ 30 | public function label() 31 | { 32 | if (config('math-captcha.text')) { 33 | return sprintf("%s %s %s", 34 | trans('mathcaptcha::math-captcha.numbers.' . $this->getMathSecondOperator()), 35 | trans('mathcaptcha::math-captcha.operands.' . $this->getMathOperand()), 36 | trans('mathcaptcha::math-captcha.numbers.' . $this->getMathFirstOperator()) 37 | ); 38 | } else { 39 | return sprintf("%d %s %d", $this->getMathSecondOperator(), $this->getMathOperand(), $this->getMathFirstOperator()); 40 | } 41 | } 42 | 43 | /** 44 | * Returns the math input field 45 | * @param array $attributes Additional HTML attributes 46 | * @return string the input field 47 | */ 48 | public function input(array $attributes = []) 49 | { 50 | $default = []; 51 | $default['type'] = 'text'; 52 | $default['id'] = 'mathcaptcha'; 53 | $default['name'] = 'mathcaptcha'; 54 | $default['required'] = 'required'; 55 | $default['value'] = old('mathcaptcha'); 56 | 57 | $attributes = array_merge($default, $attributes); 58 | 59 | $html = 'buildAttributes($attributes) . '>'; 60 | 61 | return $html; 62 | } 63 | 64 | /** 65 | * Laravel input validation 66 | * @param string $value 67 | * @return boolean 68 | */ 69 | public function verify($value) 70 | { 71 | return $value == $this->getMathResult(); 72 | } 73 | 74 | /** 75 | * Reset the math operators to regenerate a new question. 76 | * 77 | * @return void 78 | */ 79 | public function reset() 80 | { 81 | $this->session->forget('mathcaptcha.first'); 82 | $this->session->forget('mathcaptcha.second'); 83 | $this->session->forget('mathcaptcha.operand'); 84 | } 85 | 86 | /** 87 | * Operand to be used ('*','-','+') 88 | * 89 | * @return character 90 | */ 91 | protected function getMathOperand() 92 | { 93 | if (!$this->session->get('mathcaptcha.operand')) { 94 | $this->session->put( 95 | 'mathcaptcha.operand', 96 | config('math-captcha.operands.' . array_rand(config('math-captcha.operands'))) 97 | ); 98 | } 99 | 100 | return $this->session->get('mathcaptcha.operand'); 101 | } 102 | 103 | /** 104 | * The first math operand. 105 | * 106 | * @return integer 107 | */ 108 | protected function getMathFirstOperator() 109 | { 110 | if (!$this->session->get('mathcaptcha.first')) { 111 | $this->session->put( 112 | 'mathcaptcha.first', 113 | rand(config('math-captcha.rand-min'), config('math-captcha.rand-max')) 114 | ); 115 | } 116 | 117 | return $this->session->get('mathcaptcha.first'); 118 | } 119 | 120 | /** 121 | * The second math operand 122 | * @return integer 123 | */ 124 | protected function getMathSecondOperator() 125 | { 126 | if (!$this->session->get('mathcaptcha.second')) { 127 | $this->session->put( 128 | 'mathcaptcha.second', 129 | $this->getMathFirstOperator() + rand(config('math-captcha.rand-min'), config('math-captcha.rand-max')) 130 | ); 131 | } 132 | 133 | return $this->session->get('mathcaptcha.second'); 134 | } 135 | 136 | /** 137 | * The math result to be validated. 138 | * @return integer 139 | */ 140 | protected function getMathResult() 141 | { 142 | switch ($this->getMathOperand()) { 143 | case '+': 144 | return $this->getMathFirstOperator() + $this->getMathSecondOperator(); 145 | case '*': 146 | return $this->getMathFirstOperator() * $this->getMathSecondOperator(); 147 | case '-': 148 | return abs($this->getMathFirstOperator() - $this->getMathSecondOperator()); 149 | default: 150 | throw new \Exception('Math captcha uses an unknown operand.'); 151 | } 152 | } 153 | 154 | /** 155 | * Build HTML attributes. 156 | * 157 | * @param array $attributes 158 | * 159 | * @return string 160 | */ 161 | protected function buildAttributes(array $attributes) 162 | { 163 | $html = []; 164 | foreach ($attributes as $key => $value) { 165 | $html[] = $key . '="' . $value . '"'; 166 | } 167 | return count($html) ? ' ' . implode(' ', $html) : ''; 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/MathCaptchaServiceProvider.php: -------------------------------------------------------------------------------- 1 | app['validator']->extend('mathcaptcha', function ($attribute, $value) { 17 | return $this->app['mathcaptcha']->verify($value); 18 | }); 19 | 20 | $this->publishes([ 21 | __DIR__ . '/config' => config_path(), 22 | ], 'config'); 23 | 24 | $this->loadTranslationsFrom(__DIR__ . '/resources/lang', 'mathcaptcha'); 25 | $this->publishes([ 26 | __DIR__ . '/resources/lang' => resource_path('lang/vendor/mathcaptcha'), 27 | ], 'lang'); 28 | } 29 | 30 | /** 31 | * Register the application services. 32 | * 33 | * @return void 34 | */ 35 | public function register() 36 | { 37 | $this->app->singleton('mathcaptcha', function ($app) { 38 | return new MathCaptcha($this->app['session']); 39 | }); 40 | 41 | $this->mergeConfigFrom( 42 | __DIR__ . '/config/math-captcha.php', 'math-captcha' 43 | ); 44 | } 45 | 46 | /** 47 | * Get the services provided by the provider. 48 | * 49 | * @return array 50 | */ 51 | public function provides() 52 | { 53 | return ['mathcaptcha']; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/config/math-captcha.php: -------------------------------------------------------------------------------- 1 | [ 11 | '+', 12 | '-', 13 | '*', 14 | ], 15 | 16 | /** 17 | * What should be a minimum random value 18 | */ 19 | 'rand-min' => 2, 20 | 21 | /** 22 | * What should be a maximum random value 23 | */ 24 | 'rand-max' => 5, 25 | 26 | /** 27 | * Use text instead of numbers 28 | */ 29 | 'text' => false, 30 | 31 | ]; 32 | -------------------------------------------------------------------------------- /src/resources/lang/de/math-captcha.php: -------------------------------------------------------------------------------- 1 | [ 5 | '0' => 'Null', 6 | '1' => 'Eins', 7 | '2' => 'Zwei', 8 | '3' => 'Drei', 9 | '4' => 'Vier', 10 | '5' => 'Fünf', 11 | '6' => 'Sechs', 12 | '7' => 'Sieben', 13 | '8' => 'Acht', 14 | '9' => 'Neun', 15 | '10' => 'Zehn', 16 | '11' => 'Elf', 17 | '12' => 'Zwölf', 18 | ], 19 | 'operands' => [ 20 | '+' => 'plus', 21 | '-' => 'minus', 22 | '*' => 'mal', 23 | '/' => 'geteilt durch', 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /src/resources/lang/en/math-captcha.php: -------------------------------------------------------------------------------- 1 | [ 5 | '0' => 'Zero', 6 | '1' => 'One', 7 | '2' => 'Two', 8 | '3' => 'Three', 9 | '4' => 'Four', 10 | '5' => 'Five', 11 | '6' => 'Six', 12 | '7' => 'Seven', 13 | '8' => 'Eight', 14 | '9' => 'Nine', 15 | '10' => 'Ten', 16 | '11' => 'Eleven', 17 | '12' => 'Twelve', 18 | ], 19 | 'operands' => [ 20 | '+' => 'plus', 21 | '-' => 'minus', 22 | '*' => 'times', 23 | '/' => 'divided by', 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /src/resources/lang/sv/math-captcha.php: -------------------------------------------------------------------------------- 1 | [ 5 | '0' => 'Noll', 6 | '1' => 'Ett', 7 | '2' => 'Två', 8 | '3' => 'Tre', 9 | '4' => 'Fyra', 10 | '5' => 'Fem', 11 | '6' => 'Sex', 12 | '7' => 'Sju', 13 | '8' => 'Åtta', 14 | '9' => 'Nio', 15 | '10' => 'Tio', 16 | '11' => 'Elva', 17 | '12' => 'Tolv', 18 | ], 19 | 'operands' => [ 20 | '+' => 'Plus', 21 | '-' => 'Minus', 22 | '*' => 'Gånger', 23 | '/' => 'Delat med', 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elic-dev/laravel-math-captcha/8c79f79297126b43e388ac8f0ab0f7da4bb0fb5b/tests/.gitkeep -------------------------------------------------------------------------------- /tests/MathCaptchaTest.php: -------------------------------------------------------------------------------- 1 | captcha = new MathCaptcha(); 14 | } 15 | 16 | public function testDisplay() 17 | { 18 | $this->assertTrue($this->captcha instanceof MathCaptcha); 19 | } 20 | } 21 | --------------------------------------------------------------------------------