fix:修复BUG/升级1.1.6版本
This commit is contained in:
16
vendor/webman/captcha/.travis.yml
vendored
Normal file
16
vendor/webman/captcha/.travis.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3.3
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
- hhvm
|
||||
|
||||
script:
|
||||
- composer install
|
||||
- phpunit
|
||||
19
vendor/webman/captcha/LICENSE
vendored
Normal file
19
vendor/webman/captcha/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) <2012-2017> Grégoire Passault
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
142
vendor/webman/captcha/README.md
vendored
Normal file
142
vendor/webman/captcha/README.md
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
Captcha
|
||||
=======
|
||||
|
||||

|
||||
|
||||
This is a fork of [Gregwar/Captcha](https://github.com/Gregwar/Captcha)
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
With composer :
|
||||
|
||||
```
|
||||
composer require webman/captcha
|
||||
```
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
You can create a captcha with the `CaptchaBuilder` :
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
$builder = new CaptchaBuilder;
|
||||
$builder->build();
|
||||
```
|
||||
|
||||
You can then save it to a file :
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$builder->save('out.jpg');
|
||||
```
|
||||
|
||||
Or output it directly :
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
header('Content-type: image/jpeg');
|
||||
$builder->output();
|
||||
```
|
||||
|
||||
Or inline it directly in the HTML page:
|
||||
|
||||
```php
|
||||
<img src="<?php echo $builder->inline(); ?>" />
|
||||
```
|
||||
|
||||
You'll be able to get the code and compare it with a user input :
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// Example: storing the phrase in the session to test for the user
|
||||
// input later
|
||||
$_SESSION['phrase'] = $builder->getPhrase();
|
||||
```
|
||||
|
||||
You can compare the phrase with user input:
|
||||
```php
|
||||
if($builder->testPhrase($userInput)) {
|
||||
// instructions if user phrase is good
|
||||
}
|
||||
else {
|
||||
// user phrase is wrong
|
||||
}
|
||||
```
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
You can use theses functions :
|
||||
|
||||
* **__construct($phrase = null)**, constructs the builder with the given phrase, if the phrase is null, a random one will be generated
|
||||
* **getPhrase()**, allow you to get the phrase contents
|
||||
* **setDistortion($distortion)**, enable or disable the distortion, call it before `build()`
|
||||
* **isOCRReadable()**, returns `true` if the OCR can be read using the `ocrad` software, you'll need to have shell_exec enabled, imagemagick and ocrad installed
|
||||
* **buildAgainstOCR($width = 150, $height = 40, $font = null)**, builds a code until it is not readable by `ocrad`
|
||||
* **build($width = 150, $height = 40, $font = null)**, builds a code with the given $width, $height and $font. By default, a random font will be used from the library
|
||||
* **save($filename, $quality = 80)**, saves the captcha into a jpeg in the $filename, with the given quality
|
||||
* **get($quality = 80)**, returns the jpeg data
|
||||
* **output($quality = 80)**, directly outputs the jpeg code to a browser
|
||||
* **setBackgroundColor($r, $g, $b)**, sets the background color to force it (this will disable many effects and is not recommended)
|
||||
* **setBackgroundImages(array($imagepath1, $imagePath2))**, Sets custom background images to be used as captcha background. It is recommended to disable image effects when passing custom images for background (ignore_all_effects). A random image is selected from the list passed, the full paths to the image files must be passed.
|
||||
* **setInterpolation($interpolate)**, enable or disable the interpolation (enabled by default), disabling it will be quicker but the images will look uglier
|
||||
* **setIgnoreAllEffects($ignoreAllEffects)**, disable all effects on the captcha image. Recommended to use when passing custom background images for the captcha.
|
||||
* **testPhrase($phrase)**, returns true if the given phrase is good
|
||||
* **setMaxBehindLines($lines)**, sets the maximum number of lines behind the code
|
||||
* **setMaxFrontLines($lines)**, sets the maximum number of lines on the front of the code
|
||||
|
||||
If you want to change the number of character, you can call the phrase builder directly using
|
||||
extra parameters:
|
||||
|
||||
```php
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
|
||||
// Will build phrases of 3 characters
|
||||
$phraseBuilder = new PhraseBuilder(4);
|
||||
|
||||
// Will build phrases of 5 characters, only digits
|
||||
$phraseBuilder = new PhraseBuilder(5, '0123456789');
|
||||
|
||||
// Pass it as first argument of CaptchaBuilder, passing it the phrase
|
||||
// builder
|
||||
$captcha = new CaptchaBuilder(null, $phraseBuilder);
|
||||
```
|
||||
|
||||
You can also pass directly the wanted phrase to the builder:
|
||||
|
||||
```php
|
||||
// Building a Captcha with the "hello" phrase
|
||||
$captcha = new CaptchaBuilder('hello');
|
||||
```
|
||||
|
||||
Complete example
|
||||
================
|
||||
|
||||
If you want to see an example you can have a look at the ``demo/form.php``, which uses ``demo/session.php`` to
|
||||
render a captcha and check it after the submission
|
||||
|
||||
Symfony Bundle
|
||||
================
|
||||
|
||||
You can have a look at the following repository to enjoy the Symfony 2 bundle packaging this captcha generator :
|
||||
https://github.com/Gregwar/CaptchaBundle
|
||||
|
||||
Yii2 Extension
|
||||
===============
|
||||
|
||||
You can use the following extension for integrating with Yii2 Framework :
|
||||
https://github.com/juliardi/yii2-captcha
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
This library is under MIT license, have a look to the `LICENSE` file
|
||||
35
vendor/webman/captcha/composer.json
vendored
Normal file
35
vendor/webman/captcha/composer.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "webman/captcha",
|
||||
"type": "library",
|
||||
"description": "Captcha generator",
|
||||
"keywords": ["captcha", "spam", "bot"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net"
|
||||
},
|
||||
{
|
||||
"name": "Grégoire Passault",
|
||||
"email": "g.passault@gmail.com",
|
||||
"homepage": "http://www.gregwar.com/"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Livingston",
|
||||
"email": "jeremy.j.livingston@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"ext-gd": "*",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webman\\Captcha\\": "src"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.4"
|
||||
}
|
||||
}
|
||||
11
vendor/webman/captcha/demo/demo.php
vendored
Normal file
11
vendor/webman/captcha/demo/demo.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
$captcha = new CaptchaBuilder;
|
||||
$captcha
|
||||
->build()
|
||||
->save('out.jpg')
|
||||
;
|
||||
12
vendor/webman/captcha/demo/fingerprint.php
vendored
Normal file
12
vendor/webman/captcha/demo/fingerprint.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
echo count(CaptchaBuilder::create()
|
||||
->build()
|
||||
->getFingerprint()
|
||||
);
|
||||
|
||||
echo "\n";
|
||||
32
vendor/webman/captcha/demo/form.php
vendored
Normal file
32
vendor/webman/captcha/demo/form.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
|
||||
// We need the session to check the phrase after submitting
|
||||
session_start();
|
||||
?>
|
||||
|
||||
<html>
|
||||
<?php
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
// Checking that the posted phrase match the phrase stored in the session
|
||||
if (isset($_SESSION['phrase']) && PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['phrase'])) {
|
||||
echo "<h1>Captcha is valid !</h1>";
|
||||
} else {
|
||||
echo "<h1>Captcha is not valid!</h1>";
|
||||
}
|
||||
// The phrase can't be used twice
|
||||
unset($_SESSION['phrase']);
|
||||
}
|
||||
?>
|
||||
<form method="post">
|
||||
Copy the CAPTCHA:
|
||||
<?php
|
||||
// See session.php, where the captcha is actually rendered and the session phrase
|
||||
// is set accordingly to the image displayed
|
||||
?>
|
||||
<img src="session.php" />
|
||||
<input type="text" name="phrase" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</html>
|
||||
15
vendor/webman/captcha/demo/index.php
vendored
Normal file
15
vendor/webman/captcha/demo/index.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<body>
|
||||
<html>
|
||||
<meta charset="utf-8" />
|
||||
</html>
|
||||
<body>
|
||||
<h1>Captchas gallery</h1>
|
||||
<?php for ($x=0; $x<8; $x++) { ?>
|
||||
<?php for ($y=0; $y<5; $y++) { ?>
|
||||
<img src="output.php?n=<?php echo 5*$x+$y; ?>" />
|
||||
<?php } ?>
|
||||
<br />
|
||||
<?php } ?>
|
||||
</body>
|
||||
</body>
|
||||
22
vendor/webman/captcha/demo/inline.php
vendored
Normal file
22
vendor/webman/captcha/demo/inline.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
$captcha = new CaptchaBuilder();
|
||||
$captcha->build();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<body>
|
||||
<html>
|
||||
<meta charset="utf-8" />
|
||||
</html>
|
||||
<body>
|
||||
<h1>Inline Captcha</h1>
|
||||
|
||||
<img src="<?php echo $captcha->inline(); ?>"/><br/>
|
||||
Phrase: <?php echo $captcha->getPhrase(); ?>
|
||||
|
||||
</body>
|
||||
</body>
|
||||
39
vendor/webman/captcha/demo/ocr.php
vendored
Normal file
39
vendor/webman/captcha/demo/ocr.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
/**
|
||||
* Generates 1000 captchas and try to read their code with the
|
||||
* ocrad OCR
|
||||
*/
|
||||
|
||||
$tests = 10000;
|
||||
$passed = 0;
|
||||
|
||||
shell_exec('rm passed*.jpg');
|
||||
|
||||
for ($i=0; $i<$tests; $i++) {
|
||||
echo "Captcha $i/$tests... ";
|
||||
|
||||
$captcha = new CaptchaBuilder;
|
||||
|
||||
$captcha
|
||||
->setDistortion(false)
|
||||
->build()
|
||||
;
|
||||
|
||||
if ($captcha->isOCRReadable()) {
|
||||
$passed++;
|
||||
$captcha->save("passed$passed.jpg");
|
||||
echo "passed at ocr... ";
|
||||
} else {
|
||||
echo "failed... ";
|
||||
}
|
||||
|
||||
echo "pass rate: ".round(100*$passed/($i+1),2)."%\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
echo "Over, $passed/$tests readed with OCR\n";
|
||||
12
vendor/webman/captcha/demo/output.php
vendored
Normal file
12
vendor/webman/captcha/demo/output.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
header('Content-type: image/jpeg');
|
||||
|
||||
CaptchaBuilder::create()
|
||||
->build()
|
||||
->output()
|
||||
;
|
||||
22
vendor/webman/captcha/demo/session.php
vendored
Normal file
22
vendor/webman/captcha/demo/session.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// We need the session to store the correct phrase for later check
|
||||
session_start();
|
||||
|
||||
// Including the autoload (you need to composer install in the main directory)
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
// Creating the captcha instance and setting the phrase in the session to store
|
||||
// it for check when the form is submitted
|
||||
$captcha = new CaptchaBuilder;
|
||||
$_SESSION['phrase'] = $captcha->getPhrase();
|
||||
|
||||
// Setting the header to image jpeg because we here render an image
|
||||
header('Content-Type: image/jpeg');
|
||||
|
||||
// Running the actual rendering of the captcha image
|
||||
$captcha
|
||||
->build()
|
||||
->output()
|
||||
;
|
||||
15
vendor/webman/captcha/phpunit.xml.dist
vendored
Normal file
15
vendor/webman/captcha/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit colors="true" bootstrap="vendor/autoload.php">
|
||||
<testsuites>
|
||||
<testsuite name="KnpMenu Test Suite">
|
||||
<directory suffix="Test.php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
767
vendor/webman/captcha/src/CaptchaBuilder.php
vendored
Normal file
767
vendor/webman/captcha/src/CaptchaBuilder.php
vendored
Normal file
@@ -0,0 +1,767 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Captcha;
|
||||
|
||||
use AllowDynamicProperties;
|
||||
use \Exception;
|
||||
|
||||
/**
|
||||
* Builds a new captcha image
|
||||
* Uses the fingerprint parameter, if one is passed, to generate the same image
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
class CaptchaBuilder implements CaptchaBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @var int|bool
|
||||
*/
|
||||
public $background;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fingerprint = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useFingerprint = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $textColor = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $lineColor = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backgroundColor = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backgroundImages = array();
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
protected $contents = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phrase = null;
|
||||
|
||||
/**
|
||||
* @var PhraseBuilderInterface
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $distortion = true;
|
||||
|
||||
/**
|
||||
* The maximum number of lines to draw in front of
|
||||
* the image. null - use default algorithm
|
||||
*/
|
||||
protected $maxFrontLines = null;
|
||||
|
||||
/**
|
||||
* The maximum number of lines to draw behind
|
||||
* the image. null - use default algorithm
|
||||
*/
|
||||
protected $maxBehindLines = null;
|
||||
|
||||
/**
|
||||
* The maximum angle of char
|
||||
*/
|
||||
protected $maxAngle = 8;
|
||||
|
||||
/**
|
||||
* The maximum offset of char
|
||||
*/
|
||||
protected $maxOffset = 5;
|
||||
|
||||
/**
|
||||
* Is the interpolation enabled ?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $interpolation = true;
|
||||
|
||||
/**
|
||||
* Ignore all effects
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $ignoreAllEffects = false;
|
||||
|
||||
/**
|
||||
* Allowed image types for the background images
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif');
|
||||
|
||||
/**
|
||||
* The image contents
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disables the interpolation
|
||||
*
|
||||
* @param $interpolate bool True to enable, false to disable
|
||||
*
|
||||
* @return CaptchaBuilder
|
||||
*/
|
||||
public function setInterpolation($interpolate = true)
|
||||
{
|
||||
$this->interpolation = $interpolate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary dir, for OCR check
|
||||
*/
|
||||
public $tempDir = 'temp/';
|
||||
|
||||
public function __construct($phrase = null, PhraseBuilderInterface $builder = null)
|
||||
{
|
||||
if ($builder === null) {
|
||||
$this->builder = new PhraseBuilder;
|
||||
} else {
|
||||
$this->builder = $builder;
|
||||
}
|
||||
|
||||
$this->phrase = is_string($phrase) ? $phrase : $this->builder->build($phrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the phrase
|
||||
*/
|
||||
public function setPhrase($phrase)
|
||||
{
|
||||
$this->phrase = (string) $phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disable distortion
|
||||
*/
|
||||
public function setDistortion($distortion)
|
||||
{
|
||||
$this->distortion = (bool) $distortion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxBehindLines($maxBehindLines)
|
||||
{
|
||||
$this->maxBehindLines = $maxBehindLines;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxFrontLines($maxFrontLines)
|
||||
{
|
||||
$this->maxFrontLines = $maxFrontLines;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxAngle($maxAngle)
|
||||
{
|
||||
$this->maxAngle = $maxAngle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxOffset($maxOffset)
|
||||
{
|
||||
$this->maxOffset = $maxOffset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the captcha phrase
|
||||
*/
|
||||
public function getPhrase()
|
||||
{
|
||||
return $this->phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given phrase is good
|
||||
*/
|
||||
public function testPhrase($phrase)
|
||||
{
|
||||
return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiation
|
||||
*/
|
||||
public static function create($phrase = null)
|
||||
{
|
||||
return new self($phrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text color to use
|
||||
*/
|
||||
public function setTextColor($r, $g, $b)
|
||||
{
|
||||
$this->textColor = array($r, $g, $b);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background color to use
|
||||
*/
|
||||
public function setBackgroundColor($r, $g, $b)
|
||||
{
|
||||
$this->backgroundColor = array($r, $g, $b);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLineColor($r, $g, $b)
|
||||
{
|
||||
$this->lineColor = array($r, $g, $b);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ignoreAllEffects value
|
||||
*
|
||||
* @param bool $ignoreAllEffects
|
||||
* @return CaptchaBuilder
|
||||
*/
|
||||
public function setIgnoreAllEffects($ignoreAllEffects)
|
||||
{
|
||||
$this->ignoreAllEffects = $ignoreAllEffects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of background images to use (one image is randomly selected)
|
||||
*/
|
||||
public function setBackgroundImages(array $backgroundImages)
|
||||
{
|
||||
$this->backgroundImages = $backgroundImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw lines over the image
|
||||
*/
|
||||
protected function drawLine($image, $width, $height, $tcol = null)
|
||||
{
|
||||
if ($this->lineColor === null) {
|
||||
$red = $this->rand(100, 255);
|
||||
$green = $this->rand(100, 255);
|
||||
$blue = $this->rand(100, 255);
|
||||
} else {
|
||||
$red = $this->lineColor[0];
|
||||
$green = $this->lineColor[1];
|
||||
$blue = $this->lineColor[2];
|
||||
}
|
||||
|
||||
if ($tcol === null) {
|
||||
$tcol = imagecolorallocate($image, $red, $green, $blue);
|
||||
}
|
||||
|
||||
if ($this->rand(0, 1)) { // Horizontal
|
||||
$Xa = $this->rand(0, $width/2);
|
||||
$Ya = $this->rand(0, $height);
|
||||
$Xb = $this->rand($width/2, $width);
|
||||
$Yb = $this->rand(0, $height);
|
||||
} else { // Vertical
|
||||
$Xa = $this->rand(0, $width);
|
||||
$Ya = $this->rand(0, $height/2);
|
||||
$Xb = $this->rand(0, $width);
|
||||
$Yb = $this->rand($height/2, $height);
|
||||
}
|
||||
imagesetthickness($image, $this->rand(1, 3));
|
||||
imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply some post effects
|
||||
*/
|
||||
protected function postEffect($image)
|
||||
{
|
||||
if (!function_exists('imagefilter')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->backgroundColor != null || $this->textColor != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Negate ?
|
||||
if ($this->rand(0, 1) == 0) {
|
||||
imagefilter($image, IMG_FILTER_NEGATE);
|
||||
}
|
||||
|
||||
// Edge ?
|
||||
if ($this->rand(0, 10) == 0) {
|
||||
imagefilter($image, IMG_FILTER_EDGEDETECT);
|
||||
}
|
||||
|
||||
// Contrast
|
||||
imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10));
|
||||
|
||||
// Colorize
|
||||
if ($this->rand(0, 5) == 0) {
|
||||
imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the phrase on the image
|
||||
*/
|
||||
protected function writePhrase($image, $phrase, $font, $width, $height)
|
||||
{
|
||||
$length = mb_strlen($phrase);
|
||||
if ($length === 0) {
|
||||
return \imagecolorallocate($image, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Gets the text size and start position
|
||||
$size = intval($width / $length) - $this->rand(0, 3) - 1;
|
||||
$box = \imagettfbbox($size, 0, $font, $phrase);
|
||||
$textWidth = $box[2] - $box[0];
|
||||
$textHeight = $box[1] - $box[7];
|
||||
$x = intval(($width - $textWidth) / 2);
|
||||
$y = intval(($height - $textHeight) / 2) + $size;
|
||||
|
||||
if (!$this->textColor) {
|
||||
$textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150));
|
||||
} else {
|
||||
$textColor = $this->textColor;
|
||||
}
|
||||
$col = \imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]);
|
||||
|
||||
// Write the letters one by one, with random angle
|
||||
for ($i=0; $i<$length; $i++) {
|
||||
$symbol = mb_substr($phrase, $i, 1);
|
||||
$box = \imagettfbbox($size, 0, $font, $symbol);
|
||||
$w = $box[2] - $box[0];
|
||||
$angle = $this->rand(-$this->maxAngle, $this->maxAngle);
|
||||
$offset = $this->rand(-$this->maxOffset, $this->maxOffset);
|
||||
\imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $symbol);
|
||||
$x += $w;
|
||||
}
|
||||
|
||||
return $col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to read the code against an OCR
|
||||
*/
|
||||
public function isOCRReadable()
|
||||
{
|
||||
if (!is_dir($this->tempDir)) {
|
||||
@mkdir($this->tempDir, 0755, true);
|
||||
}
|
||||
|
||||
$tempj = $this->tempDir . uniqid('captcha', true) . '.jpg';
|
||||
$tempp = $this->tempDir . uniqid('captcha', true) . '.pgm';
|
||||
|
||||
$this->save($tempj);
|
||||
shell_exec("convert $tempj $tempp");
|
||||
$value = trim(strtolower(shell_exec("ocrad $tempp")));
|
||||
|
||||
@unlink($tempj);
|
||||
@unlink($tempp);
|
||||
|
||||
return $this->testPhrase($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds while the code is readable against an OCR
|
||||
*/
|
||||
public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null)
|
||||
{
|
||||
do {
|
||||
$this->build($width, $height, $font, $fingerprint);
|
||||
} while ($this->isOCRReadable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the image
|
||||
*/
|
||||
public function build($width = 150, $height = 40, $font = null, $fingerprint = null)
|
||||
{
|
||||
if (null !== $fingerprint) {
|
||||
$this->fingerprint = $fingerprint;
|
||||
$this->useFingerprint = true;
|
||||
} else {
|
||||
$this->fingerprint = array();
|
||||
$this->useFingerprint = false;
|
||||
}
|
||||
|
||||
if ($font === null) {
|
||||
$font = $this->getFontPath(__DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf');
|
||||
}
|
||||
|
||||
if (empty($this->backgroundImages)) {
|
||||
// if background images list is not set, use a color fill as a background
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
if ($this->backgroundColor == null) {
|
||||
$bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255));
|
||||
} else {
|
||||
$color = $this->backgroundColor;
|
||||
$bg = imagecolorallocate($image, $color[0], $color[1], $color[2]);
|
||||
}
|
||||
$this->background = $bg;
|
||||
imagefill($image, 0, 0, $bg);
|
||||
} else {
|
||||
// use a random background image
|
||||
$randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)];
|
||||
|
||||
$imageType = $this->validateBackgroundImage($randomBackgroundImage);
|
||||
|
||||
$image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType);
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$square = $width * $height;
|
||||
$effects = $this->rand($square/3000, $square/2000);
|
||||
|
||||
// set the maximum number of lines to draw in front of the text
|
||||
if ($this->maxBehindLines != null && $this->maxBehindLines > 0) {
|
||||
$effects = min($this->maxBehindLines, $effects);
|
||||
}
|
||||
|
||||
if ($this->maxBehindLines !== 0) {
|
||||
for ($e = 0; $e < $effects; $e++) {
|
||||
$this->drawLine($image, $width, $height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write CAPTCHA text
|
||||
$color = $this->writePhrase($image, $this->phrase, $font, $width, $height);
|
||||
|
||||
// Apply effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$square = $width * $height;
|
||||
$effects = $this->rand($square/3000, $square/2000);
|
||||
|
||||
// set the maximum number of lines to draw in front of the text
|
||||
if ($this->maxFrontLines != null && $this->maxFrontLines > 0) {
|
||||
$effects = min($this->maxFrontLines, $effects);
|
||||
}
|
||||
|
||||
if ($this->maxFrontLines !== 0) {
|
||||
for ($e = 0; $e < $effects; $e++) {
|
||||
$this->drawLine($image, $width, $height, $color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Distort the image
|
||||
if ($this->distortion && !$this->ignoreAllEffects) {
|
||||
$image = $this->distort($image, $width, $height, $bg);
|
||||
}
|
||||
|
||||
// Post effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$this->postEffect($image);
|
||||
}
|
||||
|
||||
$this->contents = $image;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $font
|
||||
* @return string
|
||||
*/
|
||||
protected function getFontPath($font)
|
||||
{
|
||||
static $fontPathMap = [];
|
||||
if (!\class_exists(\Phar::class, false) || !\Phar::running()) {
|
||||
return $font;
|
||||
}
|
||||
|
||||
$tmpPath = sys_get_temp_dir() ?: '/tmp';
|
||||
$filePath = "$tmpPath/" . basename($font);
|
||||
clearstatcache();
|
||||
if (!isset($fontPathMap[$font]) || !is_file($filePath)) {
|
||||
file_put_contents($filePath, file_get_contents($font));
|
||||
$fontPathMap[$font] = $filePath;
|
||||
}
|
||||
return $fontPathMap[$font];
|
||||
}
|
||||
|
||||
/**
|
||||
* Distorts the image
|
||||
*/
|
||||
public function distort($image, $width, $height, $bg)
|
||||
{
|
||||
$contents = imagecreatetruecolor($width, $height);
|
||||
$X = $this->rand(0, $width);
|
||||
$Y = $this->rand(0, $height);
|
||||
$phase = $this->rand(0, 10);
|
||||
$scale = 1.1 + $this->rand(0, 10000) / 30000;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$Vx = $x - $X;
|
||||
$Vy = $y - $Y;
|
||||
$Vn = sqrt($Vx * $Vx + $Vy * $Vy);
|
||||
|
||||
if ($Vn != 0) {
|
||||
$Vn2 = $Vn + 4 * sin($Vn / 30);
|
||||
$nX = $X + ($Vx * $Vn2 / $Vn);
|
||||
$nY = $Y + ($Vy * $Vn2 / $Vn);
|
||||
} else {
|
||||
$nX = $X;
|
||||
$nY = $Y;
|
||||
}
|
||||
$nY = $nY + $scale * sin($phase + $nX * 0.2);
|
||||
|
||||
if ($this->interpolation) {
|
||||
$p = $this->interpolate(
|
||||
$nX - floor($nX),
|
||||
$nY - floor($nY),
|
||||
$this->getCol($image, floor($nX), floor($nY), $bg),
|
||||
$this->getCol($image, ceil($nX), floor($nY), $bg),
|
||||
$this->getCol($image, floor($nX), ceil($nY), $bg),
|
||||
$this->getCol($image, ceil($nX), ceil($nY), $bg)
|
||||
);
|
||||
} else {
|
||||
$p = $this->getCol($image, round($nX), round($nY), $bg);
|
||||
}
|
||||
|
||||
if ($p == 0) {
|
||||
$p = $bg;
|
||||
}
|
||||
|
||||
imagesetpixel($contents, $x, $y, $p);
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Captcha to a jpeg file
|
||||
*/
|
||||
public function save($filename, $quality = 90)
|
||||
{
|
||||
imagejpeg($this->contents, $filename, $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image GD
|
||||
*/
|
||||
public function getGd()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents
|
||||
*/
|
||||
public function get($quality = 90)
|
||||
{
|
||||
ob_start();
|
||||
$this->output($quality);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML inline base64
|
||||
*/
|
||||
public function inline($quality = 90)
|
||||
{
|
||||
return 'data:image/jpeg;base64,' . base64_encode($this->get($quality));
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the image
|
||||
*/
|
||||
public function output($quality = 90)
|
||||
{
|
||||
imagejpeg($this->contents, null, $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFingerprint()
|
||||
{
|
||||
return $this->fingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random number or the next number in the
|
||||
* fingerprint
|
||||
*/
|
||||
protected function rand($min, $max)
|
||||
{
|
||||
if (!is_array($this->fingerprint)) {
|
||||
$this->fingerprint = array();
|
||||
}
|
||||
|
||||
if ($this->useFingerprint) {
|
||||
$value = current($this->fingerprint);
|
||||
next($this->fingerprint);
|
||||
} else {
|
||||
$value = mt_rand(intval($min), intval($max));
|
||||
$this->fingerprint[] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $x
|
||||
* @param $y
|
||||
* @param $nw
|
||||
* @param $ne
|
||||
* @param $sw
|
||||
* @param $se
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function interpolate($x, $y, $nw, $ne, $sw, $se)
|
||||
{
|
||||
list($r0, $g0, $b0) = $this->getRGB($nw);
|
||||
list($r1, $g1, $b1) = $this->getRGB($ne);
|
||||
list($r2, $g2, $b2) = $this->getRGB($sw);
|
||||
list($r3, $g3, $b3) = $this->getRGB($se);
|
||||
|
||||
$cx = 1.0 - $x;
|
||||
$cy = 1.0 - $y;
|
||||
|
||||
$m0 = $cx * $r0 + $x * $r1;
|
||||
$m1 = $cx * $r2 + $x * $r3;
|
||||
$r = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
$m0 = $cx * $g0 + $x * $g1;
|
||||
$m1 = $cx * $g2 + $x * $g3;
|
||||
$g = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
$m0 = $cx * $b0 + $x * $b1;
|
||||
$m1 = $cx * $b2 + $x * $b3;
|
||||
$b = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
return ($r << 16) | ($g << 8) | $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $image
|
||||
* @param $x
|
||||
* @param $y
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getCol($image, $x, $y, $background)
|
||||
{
|
||||
$L = imagesx($image);
|
||||
$H = imagesy($image);
|
||||
if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) {
|
||||
return $background;
|
||||
}
|
||||
|
||||
return imagecolorat($image, $x, $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $col
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRGB($col)
|
||||
{
|
||||
return array(
|
||||
(int) ($col >> 16) & 0xff,
|
||||
(int) ($col >> 8) & 0xff,
|
||||
(int) ($col) & 0xff,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the background image path. Return the image type if valid
|
||||
*
|
||||
* @param string $backgroundImage
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateBackgroundImage($backgroundImage)
|
||||
{
|
||||
// check if file exists
|
||||
if (!file_exists($backgroundImage)) {
|
||||
$backgroundImageExploded = explode('/', $backgroundImage);
|
||||
$imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage;
|
||||
|
||||
throw new Exception('Invalid background image: ' . $imageFileName);
|
||||
}
|
||||
|
||||
// check image type
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||
$imageType = finfo_file($finfo, $backgroundImage);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (!in_array($imageType, $this->allowedBackgroundImageTypes)) {
|
||||
throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes));
|
||||
}
|
||||
|
||||
return $imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create background image from type
|
||||
*
|
||||
* @param string $backgroundImage
|
||||
* @param string $imageType
|
||||
* @return resource
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function createBackgroundImageFromType($backgroundImage, $imageType)
|
||||
{
|
||||
switch ($imageType) {
|
||||
case 'image/jpeg':
|
||||
$image = imagecreatefromjpeg($backgroundImage);
|
||||
break;
|
||||
case 'image/png':
|
||||
$image = imagecreatefrompng($backgroundImage);
|
||||
break;
|
||||
case 'image/gif':
|
||||
$image = imagecreatefromgif($backgroundImage);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Not supported file type for background image!');
|
||||
break;
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
29
vendor/webman/captcha/src/CaptchaBuilderInterface.php
vendored
Normal file
29
vendor/webman/captcha/src/CaptchaBuilderInterface.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Captcha;
|
||||
|
||||
/**
|
||||
* A Captcha builder
|
||||
*/
|
||||
interface CaptchaBuilderInterface
|
||||
{
|
||||
/**
|
||||
* Builds the code
|
||||
*/
|
||||
public function build($width, $height, $font, $fingerprint);
|
||||
|
||||
/**
|
||||
* Saves the code to a file
|
||||
*/
|
||||
public function save($filename, $quality);
|
||||
|
||||
/**
|
||||
* Gets the image contents
|
||||
*/
|
||||
public function get($quality);
|
||||
|
||||
/**
|
||||
* Outputs the image
|
||||
*/
|
||||
public function output($quality);
|
||||
}
|
||||
BIN
vendor/webman/captcha/src/Font/captcha0.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha0.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/webman/captcha/src/Font/captcha1.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha1.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/webman/captcha/src/Font/captcha2.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha2.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/webman/captcha/src/Font/captcha3.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha3.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/webman/captcha/src/Font/captcha4.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha4.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/webman/captcha/src/Font/captcha5.ttf
vendored
Normal file
BIN
vendor/webman/captcha/src/Font/captcha5.ttf
vendored
Normal file
Binary file not shown.
78
vendor/webman/captcha/src/ImageFileHandler.php
vendored
Normal file
78
vendor/webman/captcha/src/ImageFileHandler.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Captcha;
|
||||
|
||||
/**
|
||||
* Handles actions related to captcha image files including saving and garbage collection
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
* @author Jeremy Livingston <jeremy@quizzle.com>
|
||||
*/
|
||||
class ImageFileHandler
|
||||
{
|
||||
/**
|
||||
* Name of folder for captcha images
|
||||
* @var string
|
||||
*/
|
||||
protected $imageFolder;
|
||||
|
||||
/**
|
||||
* Absolute path to public web folder
|
||||
* @var string
|
||||
*/
|
||||
protected $webPath;
|
||||
|
||||
/**
|
||||
* Frequency of garbage collection in fractions of 1
|
||||
* @var int
|
||||
*/
|
||||
protected $gcFreq;
|
||||
|
||||
/**
|
||||
* Maximum age of images in minutes
|
||||
* @var int
|
||||
*/
|
||||
protected $expiration;
|
||||
|
||||
/**
|
||||
* @param $imageFolder
|
||||
* @param $webPath
|
||||
* @param $gcFreq
|
||||
* @param $expiration
|
||||
*/
|
||||
public function __construct($imageFolder, $webPath, $gcFreq, $expiration)
|
||||
{
|
||||
$this->imageFolder = $imageFolder;
|
||||
$this->webPath = $webPath;
|
||||
$this->gcFreq = $gcFreq;
|
||||
$this->expiration = $expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the provided image content as a file
|
||||
*
|
||||
* @param string $contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function saveAsFile($contents)
|
||||
{
|
||||
$this->createFolderIfMissing();
|
||||
|
||||
$filename = md5(uniqid()) . '.jpg';
|
||||
$filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
|
||||
imagejpeg($contents, $filePath, 15);
|
||||
|
||||
return '/' . $this->imageFolder . '/' . $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the folder if it doesn't exist
|
||||
*/
|
||||
protected function createFolderIfMissing()
|
||||
{
|
||||
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
|
||||
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
vendor/webman/captcha/src/PhraseBuilder.php
vendored
Normal file
75
vendor/webman/captcha/src/PhraseBuilder.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Captcha;
|
||||
|
||||
/**
|
||||
* Generates random phrase
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
class PhraseBuilder implements PhraseBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $length;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $charset;
|
||||
/**
|
||||
* Constructs a PhraseBuilder with given parameters
|
||||
*/
|
||||
public function __construct($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||
{
|
||||
$this->length = $length;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random phrase of given length with given charset
|
||||
*/
|
||||
public function build($length = null, $charset = null)
|
||||
{
|
||||
if ($length !== null) {
|
||||
$this->length = $length;
|
||||
}
|
||||
if ($charset !== null) {
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
$phrase = '';
|
||||
$chars = str_split($this->charset);
|
||||
|
||||
for ($i = 0; $i < $this->length; $i++) {
|
||||
$phrase .= $chars[array_rand($chars)];
|
||||
}
|
||||
|
||||
return $phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Niceize" a code
|
||||
*/
|
||||
public function niceize($str)
|
||||
{
|
||||
return self::doNiceize($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* A static helper to niceize
|
||||
*/
|
||||
public static function doNiceize($str)
|
||||
{
|
||||
return strtr(strtolower($str), '01', 'ol');
|
||||
}
|
||||
|
||||
/**
|
||||
* A static helper to compare
|
||||
*/
|
||||
public static function comparePhrases($str1, $str2)
|
||||
{
|
||||
return self::doNiceize($str1) === self::doNiceize($str2);
|
||||
}
|
||||
}
|
||||
21
vendor/webman/captcha/src/PhraseBuilderInterface.php
vendored
Normal file
21
vendor/webman/captcha/src/PhraseBuilderInterface.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Captcha;
|
||||
|
||||
/**
|
||||
* Interface for the PhraseBuilder
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
interface PhraseBuilderInterface
|
||||
{
|
||||
/**
|
||||
* Generates random phrase of given length with given charset
|
||||
*/
|
||||
public function build();
|
||||
|
||||
/**
|
||||
* "Niceize" a code
|
||||
*/
|
||||
public function niceize($str);
|
||||
}
|
||||
30
vendor/webman/captcha/tests/CaptchaBuilderTest.php
vendored
Normal file
30
vendor/webman/captcha/tests/CaptchaBuilderTest.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Test;
|
||||
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CaptchaBuilderTest extends TestCase
|
||||
{
|
||||
public function testDemo()
|
||||
{
|
||||
$captcha = new CaptchaBuilder();
|
||||
$captcha
|
||||
->build()
|
||||
->save('out.jpg')
|
||||
;
|
||||
|
||||
$this->assertTrue(file_exists(__DIR__.'/../out.jpg'));
|
||||
}
|
||||
|
||||
public function testFingerPrint()
|
||||
{
|
||||
$int = count(CaptchaBuilder::create()
|
||||
->build()
|
||||
->getFingerprint()
|
||||
);
|
||||
|
||||
$this->assertTrue(is_int($int));
|
||||
}
|
||||
}
|
||||
11
vendor/webman/console/src/Command.php
vendored
11
vendor/webman/console/src/Command.php
vendored
@@ -4,6 +4,7 @@ namespace Webman\Console;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command as Commands;
|
||||
use support\Container;
|
||||
|
||||
class Command extends Application
|
||||
{
|
||||
@@ -24,13 +25,17 @@ class Command extends Application
|
||||
if ($file->getExtension() !== 'php') {
|
||||
continue;
|
||||
}
|
||||
// abc\def.php
|
||||
$relativePath = str_replace(str_replace('/', '\\', $path . '\\'), '', str_replace('/', '\\', $file->getRealPath()));
|
||||
$realNamespace = trim($namspace . '\\' . trim(dirname($relativePath), '.'), '\\');
|
||||
// app\command\abc
|
||||
$realNamespace = trim($namspace . '\\' . trim(dirname(str_replace('\\', DIRECTORY_SEPARATOR, $relativePath)), '.'), '\\');
|
||||
$realNamespace = str_replace('/', '\\', $realNamespace);
|
||||
// app\command\doc\def
|
||||
$class_name = trim($realNamespace . '\\' . $file->getBasename('.php'), '\\');
|
||||
if (!is_a($class_name, Commands::class, true)) {
|
||||
if (!class_exists($class_name) || !is_a($class_name, Commands::class, true)) {
|
||||
continue;
|
||||
}
|
||||
$this->add(new $class_name);
|
||||
$this->add(Container::get($class_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,17 +56,17 @@ class AppPluginCreateCommand extends Command
|
||||
{
|
||||
$base_path = base_path();
|
||||
$this->mkdir("$base_path/plugin/$name/app/controller", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/exception", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/model", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/middleware", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/view/index", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/config", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/public", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/api", 0777, true);
|
||||
$this->createFunctionsFile("$base_path/plugin/$name/app/functions.php");
|
||||
$this->createControllerFile("$base_path/plugin/$name/app/controller/IndexController.php", $name);
|
||||
$this->createViewFile("$base_path/plugin/$name/app/view/index/index.html");
|
||||
$this->createExceptionFile("$base_path/plugin/$name/app/exception/Handler.php", $name);
|
||||
$this->createConfigFiles("$base_path/plugin/$name/config", $name);
|
||||
$this->createApiFiles("$base_path/plugin/$name/api", $name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,44 +139,6 @@ EOF;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function createExceptionFile($path, $name)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
namespace plugin\\$name\\app\\exception;
|
||||
|
||||
use Throwable;
|
||||
use Webman\\Http\\Request;
|
||||
use Webman\\Http\\Response;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
* @package Support\Exception
|
||||
*/
|
||||
class Handler extends \\support\\exception\\Handler
|
||||
{
|
||||
public function render(Request \$request, Throwable \$exception): Response
|
||||
{
|
||||
\$code = \$exception->getCode();
|
||||
if (\$request->expectsJson()) {
|
||||
\$json = ['code' => \$code ? \$code : 500, 'message' => \$this->_debug ? \$exception->getMessage() : 'Server internal error', 'type' => 'failed'];
|
||||
\$this->_debug && \$json['traces'] = (string)\$exception;
|
||||
return new Response(200, ['Content-Type' => 'application/json'],
|
||||
\json_encode(\$json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
\$error = \$this->_debug ? \\nl2br((string)\$exception) : 'Server internal error';
|
||||
return new Response(500, [], \$error);
|
||||
}
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($path, $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
@@ -196,6 +158,119 @@ EOF;
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $base
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createApiFiles($base, $name)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace plugin\\$name\api;
|
||||
|
||||
use plugin\admin\api\Menu;
|
||||
|
||||
class Install
|
||||
{
|
||||
/**
|
||||
* 安装
|
||||
*
|
||||
* @param \$version
|
||||
* @return void
|
||||
*/
|
||||
public static function install(\$version)
|
||||
{
|
||||
// 导入菜单
|
||||
if(\$menus = static::getMenus()) {
|
||||
Menu::import(\$menus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载
|
||||
*
|
||||
* @param \$version
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall(\$version)
|
||||
{
|
||||
// 删除菜单
|
||||
foreach (static::getMenus() as \$menu) {
|
||||
Menu::delete(\$menu['key']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*
|
||||
* @param \$from_version
|
||||
* @param \$to_version
|
||||
* @param \$context
|
||||
* @return void
|
||||
*/
|
||||
public static function update(\$from_version, \$to_version, \$context = null)
|
||||
{
|
||||
// 删除不用的菜单
|
||||
if (isset(\$context['previous_menus'])) {
|
||||
static::removeUnnecessaryMenus(\$context['previous_menus']);
|
||||
}
|
||||
// 导入新菜单
|
||||
if (\$menus = static::getMenus()) {
|
||||
Menu::import(\$menus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前数据收集等
|
||||
*
|
||||
* @param \$from_version
|
||||
* @param \$to_version
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function beforeUpdate(\$from_version, \$to_version)
|
||||
{
|
||||
// 在更新之前获得老菜单,通过context传递给 update
|
||||
return ['previous_menus' => static::getMenus()];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getMenus()
|
||||
{
|
||||
clearstatcache();
|
||||
if (is_file(\$menu_file = __DIR__ . '/../config/menu.php')) {
|
||||
\$menus = include \$menu_file;
|
||||
return \$menus ?: [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除不需要的菜单
|
||||
*
|
||||
* @param \$previous_menus
|
||||
* @return void
|
||||
*/
|
||||
public static function removeUnnecessaryMenus(\$previous_menus)
|
||||
{
|
||||
\$menus_to_remove = array_diff(Menu::column(\$previous_menus, 'name'), Menu::column(static::getMenus(), 'name'));
|
||||
foreach (\$menus_to_remove as \$name) {
|
||||
Menu::delete(\$name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
EOF;
|
||||
|
||||
file_put_contents("$base/Install.php", $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $base
|
||||
* @param $name
|
||||
@@ -212,12 +287,22 @@ use support\\Request;
|
||||
return [
|
||||
'debug' => true,
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => true,
|
||||
'controller_reuse' => false,
|
||||
'version' => '1.0.0'
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/app.php", $content);
|
||||
|
||||
// menu.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/menu.php", $content);
|
||||
|
||||
// autoload.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
@@ -251,7 +336,7 @@ EOF;
|
||||
<?php
|
||||
|
||||
return [
|
||||
'' => \\plugin\\$name\\app\\exception\\Handler::class,
|
||||
'' => support\\exception\\Handler::class,
|
||||
];
|
||||
|
||||
EOF;
|
||||
@@ -351,7 +436,7 @@ return [
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => "$base/resource/translations",
|
||||
'path' => base_path() . "/plugin/$name/resource/translations",
|
||||
];
|
||||
|
||||
EOF;
|
||||
|
||||
42
vendor/webman/console/src/Commands/AppPluginInstallCommand.php
vendored
Normal file
42
vendor/webman/console/src/Commands/AppPluginInstallCommand.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginInstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app-plugin:install';
|
||||
protected static $defaultDescription = 'App Plugin Install';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Install App Plugin $name");
|
||||
$class = "\\plugin\\$name\\api\\Install";
|
||||
if (!method_exists($class, 'install')) {
|
||||
throw new \RuntimeException("Method $class::install not exists");
|
||||
}
|
||||
call_user_func([$class, 'install'], config("plugin.$name.app.version"));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
42
vendor/webman/console/src/Commands/AppPluginUninstallCommand.php
vendored
Normal file
42
vendor/webman/console/src/Commands/AppPluginUninstallCommand.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginUninstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app-plugin:uninstall';
|
||||
protected static $defaultDescription = 'App Plugin Uninstall';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Uninstall App Plugin $name");
|
||||
$class = "\\plugin\\$name\\api\\Install";
|
||||
if (!method_exists($class, 'uninstall')) {
|
||||
throw new \RuntimeException("Method $class::uninstall not exists");
|
||||
}
|
||||
call_user_func([$class, 'uninstall'], config("plugin.$name.app.version"));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
128
vendor/webman/console/src/Commands/BuildBinCommand.php
vendored
Normal file
128
vendor/webman/console/src/Commands/BuildBinCommand.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Phar;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZipArchive;
|
||||
|
||||
|
||||
class BuildBinCommand extends BuildPharCommand
|
||||
{
|
||||
protected static $defaultName = 'build:bin';
|
||||
protected static $defaultDescription = 'build bin';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('version', InputArgument::OPTIONAL, 'PHP version');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->checkEnv();
|
||||
|
||||
$output->writeln('Phar packing...');
|
||||
|
||||
$version = $input->getArgument('version');
|
||||
if (!$version) {
|
||||
$version = (float)PHP_VERSION;
|
||||
}
|
||||
$version = $version >= 8.0 ? $version : 8.1;
|
||||
$supportZip = class_exists(ZipArchive::class);
|
||||
$microZipFileName = $supportZip ? "php$version.micro.sfx.zip" : "php$version.micro.sfx";
|
||||
$pharFileName = config('plugin.webman.console.app.phar_filename', 'webman.phar');
|
||||
$binFileName = config('plugin.webman.console.app.bin_filename', 'webman.bin');
|
||||
$this->buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');
|
||||
|
||||
$binFile = "$this->buildDir/$binFileName";
|
||||
$pharFile = "$this->buildDir/$pharFileName";
|
||||
$zipFile = "$this->buildDir/$microZipFileName";
|
||||
$sfxFile = "$this->buildDir/php$version.micro.sfx";
|
||||
|
||||
// 打包
|
||||
$command = new BuildPharCommand();
|
||||
$command->execute($input, $output);
|
||||
|
||||
// 下载 micro.sfx.zip
|
||||
if (!is_file($sfxFile) && !is_file($zipFile)) {
|
||||
$domain = 'download.workerman.net';
|
||||
$output->writeln("\r\nDownloading PHP$version ...");
|
||||
if (extension_loaded('openssl')) {
|
||||
$client = stream_socket_client("ssl://$domain:443");
|
||||
} else {
|
||||
$client = stream_socket_client("tcp://$domain:80");
|
||||
}
|
||||
|
||||
fwrite($client, "GET /php/$microZipFileName HTTP/1.0\r\nAccept: text/html\r\nHost: $domain\r\nUser-Agent: webman/console\r\n\r\n");
|
||||
$bodyLength = 0;
|
||||
$bodyBuffer = '';
|
||||
$lastPercent = 0;
|
||||
while (true) {
|
||||
$buffer = fread($client, 65535);
|
||||
if ($buffer !== false) {
|
||||
$bodyBuffer .= $buffer;
|
||||
if (!$bodyLength && $pos = strpos($bodyBuffer, "\r\n\r\n")) {
|
||||
if (!preg_match('/Content-Length: (\d+)\r\n/', $bodyBuffer, $match)) {
|
||||
$output->writeln("Download php$version.micro.sfx.zip failed");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$firstLine = substr($bodyBuffer, 9, strpos($bodyBuffer, "\r\n") - 9);
|
||||
if (!preg_match('/200 /', $bodyBuffer)) {
|
||||
$output->writeln("Download php$version.micro.sfx.zip failed, $firstLine");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$bodyLength = (int)$match[1];
|
||||
$bodyBuffer = substr($bodyBuffer, $pos + 4);
|
||||
}
|
||||
}
|
||||
$receiveLength = strlen($bodyBuffer);
|
||||
$percent = ceil($receiveLength * 100 / $bodyLength);
|
||||
if ($percent != $lastPercent) {
|
||||
echo '[' . str_pad('', $percent, '=') . '>' . str_pad('', 100 - $percent) . "$percent%]";
|
||||
echo $percent < 100 ? "\r" : "\n";
|
||||
}
|
||||
$lastPercent = $percent;
|
||||
if ($bodyLength && $receiveLength >= $bodyLength) {
|
||||
file_put_contents($zipFile, $bodyBuffer);
|
||||
break;
|
||||
}
|
||||
if ($buffer === false || !is_resource($client) || feof($client)) {
|
||||
$output->writeln("Fail donwload PHP$version ...");
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$output->writeln("\r\nUse PHP$version ...");
|
||||
}
|
||||
|
||||
// 解压
|
||||
if (!is_file($sfxFile) && $supportZip) {
|
||||
$zip = new ZipArchive;
|
||||
$zip->open($zipFile, ZipArchive::CHECKCONS);
|
||||
$zip->extractTo($this->buildDir);
|
||||
}
|
||||
|
||||
// 生成二进制文件
|
||||
file_put_contents($binFile, file_get_contents($sfxFile));
|
||||
file_put_contents($binFile, file_get_contents($pharFile), FILE_APPEND);
|
||||
|
||||
// 添加执行权限
|
||||
chmod($binFile, 0755);
|
||||
|
||||
$output->writeln("\r\nSaved $binFileName to $binFile\r\nBuild Success!\r\n");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,37 @@
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Phar;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Phar;
|
||||
use RuntimeException;
|
||||
|
||||
class PharPackCommand extends Command
|
||||
class BuildPharCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'phar:pack';
|
||||
protected static $defaultName = 'build:phar';
|
||||
protected static $defaultDescription = 'Can be easily packaged a project into phar files. Easy to distribute and use.';
|
||||
|
||||
protected $buildDir = '';
|
||||
|
||||
public function __construct(string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @deprecated 暂时保留 phar:pack 命令,下一个版本再取消
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setAliases([
|
||||
'phar:pack',
|
||||
]);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
@@ -21,28 +41,24 @@ class PharPackCommand extends Command
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->checkEnv();
|
||||
|
||||
$phar_file_output_dir = config('plugin.webman.console.app.phar_file_output_dir');
|
||||
if (empty($phar_file_output_dir)) {
|
||||
throw new RuntimeException('Please set the phar file output directory.');
|
||||
}
|
||||
if (!file_exists($phar_file_output_dir) && !is_dir($phar_file_output_dir)) {
|
||||
if (!mkdir($phar_file_output_dir,0777,true)) {
|
||||
if (!file_exists($this->buildDir) && !is_dir($this->buildDir)) {
|
||||
if (!mkdir($this->buildDir,0777,true)) {
|
||||
throw new RuntimeException("Failed to create phar file output directory. Please check the permission.");
|
||||
}
|
||||
}
|
||||
|
||||
$phar_filename = config('plugin.webman.console.app.phar_filename');
|
||||
$phar_filename = config('plugin.webman.console.app.phar_filename', 'webman.phar');
|
||||
if (empty($phar_filename)) {
|
||||
throw new RuntimeException('Please set the phar filename.');
|
||||
}
|
||||
|
||||
$phar_file = rtrim($phar_file_output_dir,DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $phar_filename;
|
||||
$phar_file = rtrim($this->buildDir,DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $phar_filename;
|
||||
if (file_exists($phar_file)) {
|
||||
unlink($phar_file);
|
||||
}
|
||||
|
||||
$exclude_pattern = config('plugin.webman.console.app.exclude_pattern');
|
||||
$exclude_pattern = config('plugin.webman.console.app.exclude_pattern','');
|
||||
|
||||
$phar = new Phar($phar_file,0,'webman');
|
||||
|
||||
$phar->startBuffering();
|
||||
@@ -66,8 +82,29 @@ class PharPackCommand extends Command
|
||||
|
||||
$phar->buildFromDirectory(BASE_PATH,$exclude_pattern);
|
||||
|
||||
$exclude_files = config('plugin.webman.console.app.exclude_files');
|
||||
|
||||
$exclude_files = config('plugin.webman.console.app.exclude_files',[]);
|
||||
// 打包生成的phar和bin文件是面向生产环境的,所以以下这些命令没有任何意义,执行的话甚至会出错,需要排除在外。
|
||||
$exclude_command_files = [
|
||||
'AppPluginCreateCommand.php',
|
||||
'BuildBinCommand.php',
|
||||
'BuildPharCommand.php',
|
||||
'MakeBootstrapCommand.php',
|
||||
'MakeCommandCommand.php',
|
||||
'MakeControllerCommand.php',
|
||||
'MakeMiddlewareCommand.php',
|
||||
'MakeModelCommand.php',
|
||||
'PluginCreateCommand.php',
|
||||
'PluginDisableCommand.php',
|
||||
'PluginEnableCommand.php',
|
||||
'PluginExportCommand.php',
|
||||
'PluginInstallCommand.php',
|
||||
'PluginUninstallCommand.php'
|
||||
];
|
||||
$exclude_command_files = array_map(function ($cmd_file) {
|
||||
return 'vendor/webman/console/src/Commands/'.$cmd_file;
|
||||
},$exclude_command_files);
|
||||
$exclude_files = array_unique(array_merge($exclude_command_files,$exclude_files));
|
||||
foreach ($exclude_files as $file) {
|
||||
if($phar->offsetExists($file)){
|
||||
$phar->delete($file);
|
||||
@@ -94,16 +131,18 @@ __HALT_COMPILER();
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function checkEnv(): void
|
||||
public function checkEnv(): void
|
||||
{
|
||||
if (!class_exists(Phar::class, false)) {
|
||||
throw new RuntimeException("The 'phar' extension is required for build phar package");
|
||||
}
|
||||
|
||||
if (ini_get('phar.readonly')) {
|
||||
$command = static::$defaultName;
|
||||
throw new RuntimeException(
|
||||
"The 'phar.readonly' is 'On', build phar must setting it 'Off' or exec with 'php -d phar.readonly=0 ./webman phar:pack'"
|
||||
"The 'phar.readonly' is 'On', build phar must setting it 'Off' or exec with 'php -d phar.readonly=0 ./webman $command'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -21,6 +22,7 @@ class MakeModelCommand extends Command
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Model name');
|
||||
$this->addArgument('type', InputArgument::OPTIONAL, 'Type');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,6 +34,7 @@ class MakeModelCommand extends Command
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$name = Util::nameToClass($name);
|
||||
$type = $input->getArgument('type');
|
||||
$output->writeln("Make model $name");
|
||||
if (!($pos = strrpos($name, '/'))) {
|
||||
$name = ucfirst($name);
|
||||
@@ -58,15 +61,18 @@ class MakeModelCommand extends Command
|
||||
$file = app_path() . "/$path/$name.php";
|
||||
$namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
|
||||
}
|
||||
$database = config('database');
|
||||
if (isset($database['default']) && strpos($database['default'], 'plugin.') === 0) {
|
||||
$database = false;
|
||||
if (!$type) {
|
||||
$database = config('database');
|
||||
if (isset($database['default']) && strpos($database['default'], 'plugin.') === 0) {
|
||||
$database = false;
|
||||
}
|
||||
$thinkorm = config('thinkorm');
|
||||
if (isset($thinkorm['default']) && strpos($thinkorm['default'], 'plugin.') === 0) {
|
||||
$thinkorm = false;
|
||||
}
|
||||
$type = !$database && $thinkorm ? 'tp' : 'laravel';
|
||||
}
|
||||
$thinkorm = config('thinkorm');
|
||||
if (isset($thinkorm['default']) && strpos($thinkorm['default'], 'plugin.') === 0) {
|
||||
$thinkorm = false;
|
||||
}
|
||||
if (!$database && $thinkorm) {
|
||||
if ($type == 'tp') {
|
||||
$this->createTpModel($name, $namespace, $file);
|
||||
} else {
|
||||
$this->createModel($name, $namespace, $file);
|
||||
@@ -94,8 +100,10 @@ class MakeModelCommand extends Command
|
||||
try {
|
||||
$prefix = config('database.connections.mysql.prefix') ?? '';
|
||||
$database = config('database.connections.mysql.database');
|
||||
if (\support\Db::select("show tables like '{$prefix}{$table}s'")) {
|
||||
$table = "{$prefix}{$table}s";
|
||||
$inflector = InflectorFactory::create()->build();
|
||||
$table_plura = $inflector->pluralize($inflector->tableize($class));
|
||||
if (\support\Db::select("show tables like '{$prefix}{$table_plura}'")) {
|
||||
$table = "{$prefix}{$table_plura}";
|
||||
} else if (\support\Db::select("show tables like '{$prefix}{$table}'")) {
|
||||
$table_val = "'$table'";
|
||||
$table = "{$prefix}{$table}";
|
||||
|
||||
20
vendor/webman/console/src/Install.php
vendored
20
vendor/webman/console/src/Install.php
vendored
@@ -8,9 +8,9 @@ class Install
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $pathRelation = array (
|
||||
'config/plugin/webman/console' => 'config/plugin/webman/console',
|
||||
);
|
||||
protected static $pathRelation = [
|
||||
'config/plugin/webman/console' => 'config/plugin/webman/console',
|
||||
];
|
||||
|
||||
/**
|
||||
* Install
|
||||
@@ -18,8 +18,13 @@ class Install
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
copy(__DIR__ . "/webman", base_path()."/webman");
|
||||
chmod(base_path()."/webman", 0755);
|
||||
$dest = base_path() . "/webman";
|
||||
if (is_dir($dest)) {
|
||||
echo "Installation failed, please remove directory $dest\n";
|
||||
return;
|
||||
}
|
||||
copy(__DIR__ . "/webman", $dest);
|
||||
chmod(base_path() . "/webman", 0755);
|
||||
|
||||
static::installByRelation();
|
||||
}
|
||||
@@ -33,7 +38,6 @@ class Install
|
||||
if (is_file(base_path()."/webman")) {
|
||||
unlink(base_path() . "/webman");
|
||||
}
|
||||
|
||||
self::uninstallByRelation();
|
||||
}
|
||||
|
||||
@@ -50,7 +54,6 @@ class Install
|
||||
mkdir($parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
//symlink(__DIR__ . "/$source", base_path()."/$dest");
|
||||
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
|
||||
}
|
||||
}
|
||||
@@ -66,9 +69,6 @@ class Install
|
||||
if (!is_dir($path) && !is_file($path)) {
|
||||
continue;
|
||||
}
|
||||
/*if (is_link($path) {
|
||||
unlink($path);
|
||||
}*/
|
||||
remove_dir($path);
|
||||
}
|
||||
}
|
||||
|
||||
8
vendor/webman/console/src/Util.php
vendored
8
vendor/webman/console/src/Util.php
vendored
@@ -16,8 +16,10 @@ class Util
|
||||
|
||||
public static function classToName($class)
|
||||
{
|
||||
$inflector = InflectorFactory::create()->build();
|
||||
return $inflector->pluralize($inflector->tableize($class));
|
||||
$class = lcfirst($class);
|
||||
return preg_replace_callback(['/([A-Z])/'], function ($matches) {
|
||||
return '_' . strtolower($matches[1]);
|
||||
}, $class);
|
||||
}
|
||||
|
||||
public static function nameToClass($class)
|
||||
@@ -59,6 +61,6 @@ class Util
|
||||
}
|
||||
}
|
||||
$realname = implode(DIRECTORY_SEPARATOR, $realname);
|
||||
return $return_full_path ? realpath($base_path . DIRECTORY_SEPARATOR . $realname) : $realname;
|
||||
return $return_full_path ? get_realpath($base_path . DIRECTORY_SEPARATOR . $realname) : $realname;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
'enable' => true,
|
||||
|
||||
'phar_file_output_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build',
|
||||
'build_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build',
|
||||
|
||||
'phar_filename' => 'webman.phar',
|
||||
'phar_filename' => 'webman.phar',
|
||||
|
||||
'bin_filename' => 'webman.bin',
|
||||
|
||||
'signature_algorithm'=> Phar::SHA256, //set the signature algorithm for a phar and apply it. The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.
|
||||
|
||||
'private_key_file' => '', // The file path for certificate or OpenSSL private key file.
|
||||
|
||||
//'exclude_pattern' => '#^(?!.*(config/plugin/webman/console/app.php|webman/console/src/Commands/(PharPackCommand.php|ReloadCommand.php)|LICENSE|composer.json|.github|.idea|doc|docs|.git|.setting|runtime|test|test_old|tests|Tests|vendor-bin|.md))(.*)$#',
|
||||
'exclude_pattern' => '#^(?!.*(composer.json|/.github/|/.idea/|/.git/|/.setting/|/runtime/|/vendor-bin/|/build/|/vendor/webman/admin/))(.*)$#',
|
||||
|
||||
'exclude_files' => [
|
||||
'.env', 'LICENSE', 'composer.json', 'composer.lock','start.php'
|
||||
'.env', 'LICENSE', 'composer.json', 'composer.lock', 'start.php', 'webman.phar', 'webman.bin'
|
||||
]
|
||||
];
|
||||
|
||||
3
vendor/webman/console/src/webman
vendored
3
vendor/webman/console/src/webman
vendored
@@ -4,6 +4,7 @@
|
||||
use Webman\Config;
|
||||
use Webman\Console\Command;
|
||||
use Webman\Console\Util;
|
||||
use support\Container;
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
@@ -36,7 +37,7 @@ foreach (config('plugin', []) as $firm => $projects) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['command'] ?? [] as $command) {
|
||||
$cli->add(new $command);
|
||||
$cli->add(Container::get($command));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
vendor/webman/event/src/BootStrap.php
vendored
43
vendor/webman/event/src/BootStrap.php
vendored
@@ -7,6 +7,7 @@ use support\Log;
|
||||
|
||||
class BootStrap implements \Webman\Bootstrap
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -19,17 +20,30 @@ class BootStrap implements \Webman\Bootstrap
|
||||
public static function start($worker)
|
||||
{
|
||||
static::getEvents([config()]);
|
||||
}
|
||||
|
||||
protected static function convertCallable($callback)
|
||||
{
|
||||
if (\is_array($callback)) {
|
||||
$callback = \array_values($callback);
|
||||
if (isset($callback[1]) && \is_string($callback[0]) && \class_exists($callback[0])) {
|
||||
$callback = [Container::get($callback[0]), $callback[1]];
|
||||
foreach (static::$events as $name => $events) {
|
||||
// 支持排序,1 2 3 ... 9 a b c...z
|
||||
ksort($events, SORT_NATURAL);
|
||||
foreach ($events as $callbacks) {
|
||||
foreach ($callbacks as $callback) {
|
||||
Event::on($name, $callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $callbacks
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected static function convertCallable($callbacks)
|
||||
{
|
||||
if (\is_array($callbacks)) {
|
||||
$callback = \array_values($callbacks);
|
||||
if (isset($callback[1]) && \is_string($callback[0]) && \class_exists($callback[0])) {
|
||||
return [Container::get($callback[0]), $callback[1]];
|
||||
}
|
||||
}
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,12 +57,10 @@ class BootStrap implements \Webman\Bootstrap
|
||||
continue;
|
||||
}
|
||||
if (isset($config['event']) && is_array($config['event']) && !isset($config['event']['app']['enable'])) {
|
||||
$events = [];
|
||||
foreach ($config['event'] as $event_name => $callbacks) {
|
||||
$callbacks = static::convertCallable($callbacks);
|
||||
if (is_callable($callbacks)) {
|
||||
$events[$event_name] = [$callbacks];
|
||||
Event::on($event_name, $callbacks);
|
||||
static::$events[$event_name][] = [$callbacks];
|
||||
continue;
|
||||
}
|
||||
if (!is_array($callbacks)) {
|
||||
@@ -57,11 +69,11 @@ class BootStrap implements \Webman\Bootstrap
|
||||
Log::error($msg);
|
||||
continue;
|
||||
}
|
||||
foreach ($callbacks as $callback) {
|
||||
ksort($callbacks, SORT_NATURAL);
|
||||
foreach ($callbacks as $id => $callback) {
|
||||
$callback = static::convertCallable($callback);
|
||||
if (is_callable($callback)) {
|
||||
$events[$event_name][] = $callback;
|
||||
Event::on($event_name, $callback);
|
||||
static::$events[$event_name][$id][] = $callback;
|
||||
continue;
|
||||
}
|
||||
$msg = "Events: $event_name => " . var_export($callback, true) . " is not callable\n";
|
||||
@@ -69,7 +81,6 @@ class BootStrap implements \Webman\Bootstrap
|
||||
Log::error($msg);
|
||||
}
|
||||
}
|
||||
static::$events = array_merge_recursive(static::$events, $events);
|
||||
unset($config['event']);
|
||||
}
|
||||
static::getEvents($config);
|
||||
|
||||
28
vendor/webman/event/src/Event.php
vendored
28
vendor/webman/event/src/Event.php
vendored
@@ -63,9 +63,8 @@ class Event
|
||||
* @param bool $halt
|
||||
* @return array|null|mixed
|
||||
*/
|
||||
public static function emit($event_name, $data, $halt = false)
|
||||
public static function emit($event_name, $data, bool $halt = false)
|
||||
{
|
||||
$success_count = 0;
|
||||
$listeners = static::getListeners($event_name);
|
||||
$responses = [];
|
||||
foreach ($listeners as $listener) {
|
||||
@@ -91,6 +90,29 @@ class Event
|
||||
}
|
||||
return $halt ? null : $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @param mixed $data
|
||||
* @param bool $halt
|
||||
* @return array|null|mixed
|
||||
*/
|
||||
public static function dispatch($event_name, $data, bool $halt = false)
|
||||
{
|
||||
$listeners = static::getListeners($event_name);
|
||||
$responses = [];
|
||||
foreach ($listeners as $listener) {
|
||||
$response = $listener($data, $event_name);
|
||||
$responses[] = $response;
|
||||
if ($halt && !is_null($response)) {
|
||||
return $response;
|
||||
}
|
||||
if ($response === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $halt ? null : $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
@@ -136,4 +158,4 @@ class Event
|
||||
{
|
||||
return !empty(static::getListeners($event_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/webman/gateway-worker/composer.json
vendored
2
vendor/webman/gateway-worker/composer.json
vendored
@@ -3,7 +3,7 @@
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"workerman/gateway-worker": "^3.0"
|
||||
"workerman/gateway-worker": ">=3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
5
vendor/webman/gateway-worker/src/Gateway.php
vendored
5
vendor/webman/gateway-worker/src/Gateway.php
vendored
@@ -3,6 +3,11 @@ namespace Webman\GatewayWorker;
|
||||
|
||||
class Gateway extends \GatewayWorker\Gateway
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_autoloadRootPath = '';
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
foreach ($config as $key => $value)
|
||||
|
||||
9
vendor/webman/think-cache/src/ThinkCache.php
vendored
9
vendor/webman/think-cache/src/ThinkCache.php
vendored
@@ -5,6 +5,8 @@ namespace Webman\ThinkCache;
|
||||
use Webman\Bootstrap;
|
||||
use Workerman\Timer;
|
||||
use think\facade\Cache;
|
||||
use think\Container;
|
||||
use think\DbManager;
|
||||
|
||||
class ThinkCache implements Bootstrap
|
||||
{
|
||||
@@ -20,5 +22,10 @@ class ThinkCache implements Bootstrap
|
||||
Cache::get('ping');
|
||||
});
|
||||
}
|
||||
|
||||
if (class_exists(DbManager::class)) {
|
||||
$manager_instance = Container::getInstance()->make(DbManager::class);
|
||||
$manager_instance->setCache(Cache::store());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/webman/think-orm/composer.json
vendored
2
vendor/webman/think-orm/composer.json
vendored
@@ -4,7 +4,7 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"workerman/webman-framework": "^1.2.1",
|
||||
"topthink/think-orm": "^2.0.53"
|
||||
"topthink/think-orm": "^2.0.53 || ^3.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
13
vendor/webman/think-orm/src/ThinkOrm.php
vendored
13
vendor/webman/think-orm/src/ThinkOrm.php
vendored
@@ -29,10 +29,15 @@ class ThinkOrm implements Bootstrap
|
||||
$manager_instance = $property->getValue();
|
||||
}
|
||||
Timer::add(55, function () use ($manager_instance) {
|
||||
$reflect = new \ReflectionClass($manager_instance);
|
||||
$property = $reflect->getProperty('instance');
|
||||
$property->setAccessible(true);
|
||||
$instances = $property->getValue($manager_instance);
|
||||
$instances = [];
|
||||
if (method_exists($manager_instance, 'getInstance')) {
|
||||
$instances = $manager_instance->getInstance();
|
||||
} else {
|
||||
$reflect = new \ReflectionClass($manager_instance);
|
||||
$property = $reflect->getProperty('instance');
|
||||
$property->setAccessible(true);
|
||||
$instances = $property->getValue($manager_instance);
|
||||
}
|
||||
foreach ($instances as $connection) {
|
||||
/* @var \think\db\connector\Mysql $connection */
|
||||
if ($connection->getConfig('type') == 'mysql') {
|
||||
|
||||
4
vendor/webman/think-orm/src/_ide_helper.php
vendored
4
vendor/webman/think-orm/src/_ide_helper.php
vendored
@@ -82,7 +82,7 @@ namespace think\facade {
|
||||
* @throws \think\db\exception\BindParamException
|
||||
* @throws \PDOException
|
||||
*/
|
||||
public function query(string $sql, array $bind = []): array
|
||||
public static function query(string $sql, array $bind = []): array
|
||||
{
|
||||
/** @var \think\Db $instance */
|
||||
return $instance->query($sql,$bind);
|
||||
@@ -108,7 +108,7 @@ namespace think\facade {
|
||||
* @throws \think\db\exception\BindParamException
|
||||
* @throws \PDOException
|
||||
*/
|
||||
public function execute(string $sql, array $bind = []): int
|
||||
public static function execute(string $sql, array $bind = []): int
|
||||
{
|
||||
/** @var \think\Db $instance */
|
||||
return $instance->execute($sql,$bind);
|
||||
|
||||
Reference in New Issue
Block a user