1
This commit is contained in:
4
vendor/topthink/think-helper/.gitignore
vendored
4
vendor/topthink/think-helper/.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
/vendor/
|
||||
/.idea/
|
||||
/.idea/
|
||||
composer.lock
|
||||
.phpunit.result.cache
|
||||
7
vendor/topthink/think-helper/README.md
vendored
7
vendor/topthink/think-helper/README.md
vendored
@@ -1,10 +1,13 @@
|
||||
# thinkphp5 常用的一些扩展类库
|
||||
# thinkphp6 常用的一些扩展类库
|
||||
|
||||
> 更新完善中
|
||||
基于PHP7.1+
|
||||
|
||||
[](https://github.com/larvatecn/think-helper/actions/workflows/php.yml)
|
||||
|
||||
> 以下类库都在`\\think\\helper`命名空间下
|
||||
|
||||
## Str
|
||||
|
||||
> 字符串操作
|
||||
|
||||
```
|
||||
|
||||
21
vendor/topthink/think-helper/composer.json
vendored
21
vendor/topthink/think-helper/composer.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "topthink/think-helper",
|
||||
"description": "The ThinkPHP5 Helper Package",
|
||||
"description": "The ThinkPHP6 Helper Package",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
@@ -8,12 +8,29 @@
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\helper\\": "src"
|
||||
"think\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helper.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./vendor/bin/phpunit --colors"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"test": "Run all tests."
|
||||
}
|
||||
}
|
||||
|
||||
41
vendor/topthink/think-helper/src/Arr.php
vendored
41
vendor/topthink/think-helper/src/Arr.php
vendored
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\helper;
|
||||
|
||||
|
||||
class Arr
|
||||
{
|
||||
|
||||
public static function isAssoc(array $array)
|
||||
{
|
||||
$keys = array_keys($array);
|
||||
|
||||
return array_keys($keys) !== $keys;
|
||||
}
|
||||
|
||||
public static function sortRecursive($array)
|
||||
{
|
||||
foreach ($array as &$value) {
|
||||
if (is_array($value)) {
|
||||
$value = static::sortRecursive($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (static::isAssoc($array)) {
|
||||
ksort($array);
|
||||
} else {
|
||||
sort($array);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
202
vendor/topthink/think-helper/src/Str.php
vendored
202
vendor/topthink/think-helper/src/Str.php
vendored
@@ -1,202 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\helper;
|
||||
|
||||
class Str
|
||||
{
|
||||
|
||||
protected static $snakeCache = [];
|
||||
|
||||
protected static $camelCache = [];
|
||||
|
||||
protected static $studlyCache = [];
|
||||
|
||||
/**
|
||||
* 检查字符串中是否包含某些字符串
|
||||
* @param string $haystack
|
||||
* @param string|array $needles
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needles)
|
||||
{
|
||||
foreach ((array) $needles as $needle) {
|
||||
if ($needle != '' && mb_strpos($haystack, $needle) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否以某些字符串结尾
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string|array $needles
|
||||
* @return bool
|
||||
*/
|
||||
public static function endsWith($haystack, $needles)
|
||||
{
|
||||
foreach ((array) $needles as $needle) {
|
||||
if ((string) $needle === static::substr($haystack, -static::length($needle))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否以某些字符串开头
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string|array $needles
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needles)
|
||||
{
|
||||
foreach ((array) $needles as $needle) {
|
||||
if ($needle != '' && mb_strpos($haystack, $needle) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定长度的随机字母数字组合的字符串
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public static function random($length = 16)
|
||||
{
|
||||
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
return static::substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转小写
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function lower($value)
|
||||
{
|
||||
return mb_strtolower($value, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转大写
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function upper($value)
|
||||
{
|
||||
return mb_strtoupper($value, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串的长度
|
||||
*
|
||||
* @param string $value
|
||||
* @return int
|
||||
*/
|
||||
public static function length($value)
|
||||
{
|
||||
return mb_strlen($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $start
|
||||
* @param int|null $length
|
||||
* @return string
|
||||
*/
|
||||
public static function substr($string, $start, $length = null)
|
||||
{
|
||||
return mb_substr($string, $start, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public static function snake($value, $delimiter = '_')
|
||||
{
|
||||
$key = $value;
|
||||
|
||||
if (isset(static::$snakeCache[$key][$delimiter])) {
|
||||
return static::$snakeCache[$key][$delimiter];
|
||||
}
|
||||
|
||||
if (!ctype_lower($value)) {
|
||||
$value = preg_replace('/\s+/u', '', $value);
|
||||
|
||||
$value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
|
||||
}
|
||||
|
||||
return static::$snakeCache[$key][$delimiter] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰(首字母小写)
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function camel($value)
|
||||
{
|
||||
if (isset(static::$camelCache[$value])) {
|
||||
return static::$camelCache[$value];
|
||||
}
|
||||
|
||||
return static::$camelCache[$value] = lcfirst(static::studly($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰(首字母大写)
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function studly($value)
|
||||
{
|
||||
$key = $value;
|
||||
|
||||
if (isset(static::$studlyCache[$key])) {
|
||||
return static::$studlyCache[$key];
|
||||
}
|
||||
|
||||
$value = ucwords(str_replace(['-', '_'], ' ', $value));
|
||||
|
||||
return static::$studlyCache[$key] = str_replace(' ', '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为首字母大写的标题格式
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function title($value)
|
||||
{
|
||||
return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
|
||||
}
|
||||
}
|
||||
319
vendor/topthink/think-helper/src/helper.php
vendored
319
vendor/topthink/think-helper/src/helper.php
vendored
@@ -9,30 +9,309 @@
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\Collection;
|
||||
use think\helper\Arr;
|
||||
|
||||
if (!function_exists('classnames')) {
|
||||
if (!function_exists('throw_if')) {
|
||||
/**
|
||||
* css样式名生成器
|
||||
* classnames("foo", "bar"); // => "foo bar"
|
||||
* classnames("foo", [ "bar"=> true ]); // => "foo bar"
|
||||
* classnames([ "foo-bar"=> true ]); // => "foo-bar"
|
||||
* classnames([ "foo-bar"=> false ]); // => "
|
||||
* classnames([ "foo" => true ], [ "bar"=> true ]); // => "foo bar"
|
||||
* classnames([ "foo" => true, "bar"=> true ]); // => "foo bar"
|
||||
* classnames("foo", [ "bar"=> true, "duck"=> false ], "baz", [ "quux"=> true ]); // => "foo bar baz quux"
|
||||
* classnames(null, false, "bar", 0, 1, [ "baz"=> null ]); // => "bar 1"
|
||||
* 按条件抛异常
|
||||
*
|
||||
* @template TValue
|
||||
* @template TException of \Throwable
|
||||
*
|
||||
* @param TValue $condition
|
||||
* @param TException|class-string<TException>|string $exception
|
||||
* @param mixed ...$parameters
|
||||
* @return TValue
|
||||
*
|
||||
* @throws TException
|
||||
*/
|
||||
function classnames()
|
||||
function throw_if($condition, $exception, ...$parameters)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$classes = array_map(function ($arg) {
|
||||
if (is_array($arg)) {
|
||||
return implode(" ", array_filter(array_map(function ($expression, $class) {
|
||||
return $expression ? $class : false;
|
||||
}, $arg, array_keys($arg))));
|
||||
if ($condition) {
|
||||
throw (is_string($exception) ? new $exception(...$parameters) : $exception);
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('throw_unless')) {
|
||||
/**
|
||||
* 按条件抛异常
|
||||
*
|
||||
* @template TValue
|
||||
* @template TException of \Throwable
|
||||
*
|
||||
* @param TValue $condition
|
||||
* @param TException|class-string<TException>|string $exception
|
||||
* @param mixed ...$parameters
|
||||
* @return TValue
|
||||
*
|
||||
* @throws TException
|
||||
*/
|
||||
function throw_unless($condition, $exception, ...$parameters)
|
||||
{
|
||||
if (!$condition) {
|
||||
throw (is_string($exception) ? new $exception(...$parameters) : $exception);
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('tap')) {
|
||||
/**
|
||||
* 对一个值调用给定的闭包,然后返回该值
|
||||
*
|
||||
* @template TValue
|
||||
*
|
||||
* @param TValue $value
|
||||
* @param (callable(TValue): mixed)|null $callback
|
||||
* @return TValue
|
||||
*/
|
||||
function tap($value, $callback = null)
|
||||
{
|
||||
if (is_null($callback)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$callback($value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('value')) {
|
||||
/**
|
||||
* Return the default value of the given value.
|
||||
*
|
||||
* @template TValue
|
||||
*
|
||||
* @param TValue|\Closure(): TValue $value
|
||||
* @return TValue
|
||||
*/
|
||||
function value($value)
|
||||
{
|
||||
return $value instanceof Closure ? $value() : $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('collect')) {
|
||||
/**
|
||||
* Create a collection from the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return Collection
|
||||
*/
|
||||
function collect($value = null)
|
||||
{
|
||||
return new Collection($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('data_fill')) {
|
||||
/**
|
||||
* Fill in data where it's missing.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
function data_fill(&$target, $key, $value)
|
||||
{
|
||||
return data_set($target, $key, $value, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('data_get')) {
|
||||
/**
|
||||
* Get an item from an array or object using "dot" notation.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array|int $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
function data_get($target, $key, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
$key = is_array($key) ? $key : explode('.', $key);
|
||||
|
||||
while (!is_null($segment = array_shift($key))) {
|
||||
if ('*' === $segment) {
|
||||
if ($target instanceof Collection) {
|
||||
$target = $target->all();
|
||||
} elseif (!is_array($target)) {
|
||||
return value($default);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($target as $item) {
|
||||
$result[] = data_get($item, $key);
|
||||
}
|
||||
|
||||
return in_array('*', $key) ? Arr::collapse($result) : $result;
|
||||
}
|
||||
return $arg;
|
||||
}, $args);
|
||||
return implode(" ", array_filter($classes));
|
||||
|
||||
if (Arr::accessible($target) && Arr::exists($target, $segment)) {
|
||||
$target = $target[$segment];
|
||||
} elseif (is_object($target) && isset($target->{$segment})) {
|
||||
$target = $target->{$segment};
|
||||
} else {
|
||||
return value($default);
|
||||
}
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('data_set')) {
|
||||
/**
|
||||
* Set an item on an array or object using dot notation.
|
||||
*
|
||||
* @param mixed $target
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @param bool $overwrite
|
||||
* @return mixed
|
||||
*/
|
||||
function data_set(&$target, $key, $value, $overwrite = true)
|
||||
{
|
||||
$segments = is_array($key) ? $key : explode('.', $key);
|
||||
|
||||
if (($segment = array_shift($segments)) === '*') {
|
||||
if (!Arr::accessible($target)) {
|
||||
$target = [];
|
||||
}
|
||||
|
||||
if ($segments) {
|
||||
foreach ($target as &$inner) {
|
||||
data_set($inner, $segments, $value, $overwrite);
|
||||
}
|
||||
} elseif ($overwrite) {
|
||||
foreach ($target as &$inner) {
|
||||
$inner = $value;
|
||||
}
|
||||
}
|
||||
} elseif (Arr::accessible($target)) {
|
||||
if ($segments) {
|
||||
if (!Arr::exists($target, $segment)) {
|
||||
$target[$segment] = [];
|
||||
}
|
||||
|
||||
data_set($target[$segment], $segments, $value, $overwrite);
|
||||
} elseif ($overwrite || !Arr::exists($target, $segment)) {
|
||||
$target[$segment] = $value;
|
||||
}
|
||||
} elseif (is_object($target)) {
|
||||
if ($segments) {
|
||||
if (!isset($target->{$segment})) {
|
||||
$target->{$segment} = [];
|
||||
}
|
||||
|
||||
data_set($target->{$segment}, $segments, $value, $overwrite);
|
||||
} elseif ($overwrite || !isset($target->{$segment})) {
|
||||
$target->{$segment} = $value;
|
||||
}
|
||||
} else {
|
||||
$target = [];
|
||||
|
||||
if ($segments) {
|
||||
data_set($target[$segment], $segments, $value, $overwrite);
|
||||
} elseif ($overwrite) {
|
||||
$target[$segment] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('trait_uses_recursive')) {
|
||||
/**
|
||||
* 获取一个trait里所有引用到的trait
|
||||
*
|
||||
* @param string $trait Trait
|
||||
* @return array
|
||||
*/
|
||||
function trait_uses_recursive(string $trait): array
|
||||
{
|
||||
$traits = class_uses($trait);
|
||||
foreach ($traits as $trait) {
|
||||
$traits += trait_uses_recursive($trait);
|
||||
}
|
||||
|
||||
return $traits;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('class_basename')) {
|
||||
/**
|
||||
* 获取类名(不包含命名空间)
|
||||
*
|
||||
* @param mixed $class 类名
|
||||
* @return string
|
||||
*/
|
||||
function class_basename($class): string
|
||||
{
|
||||
$class = is_object($class) ? get_class($class) : $class;
|
||||
return basename(str_replace('\\', '/', $class));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('class_uses_recursive')) {
|
||||
/**
|
||||
*获取一个类里所有用到的trait,包括父类的
|
||||
*
|
||||
* @param mixed $class 类名
|
||||
* @return array
|
||||
*/
|
||||
function class_uses_recursive($class): array
|
||||
{
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$classes = array_merge([$class => $class], class_parents($class));
|
||||
foreach ($classes as $class) {
|
||||
$results += trait_uses_recursive($class);
|
||||
}
|
||||
|
||||
return array_unique($results);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('array_is_list')) {
|
||||
/**
|
||||
* 判断数组是否为list
|
||||
*
|
||||
* @param array $array 数据
|
||||
* @return bool
|
||||
*/
|
||||
function array_is_list(array $array): bool
|
||||
{
|
||||
return array_values($array) === $array;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('json_validate')) {
|
||||
/**
|
||||
* 判断是否为有效json数据
|
||||
*
|
||||
* @param string $string 数据
|
||||
* @return bool
|
||||
*/
|
||||
function json_validate(string $string): bool
|
||||
{
|
||||
json_decode($string);
|
||||
return json_last_error() === JSON_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
122
vendor/topthink/think-image/src/Image.php
vendored
122
vendor/topthink/think-image/src/Image.php
vendored
@@ -140,6 +140,31 @@ class Image
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* http输出图片
|
||||
* @return void
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
$type = $this->info['type'];
|
||||
header("content-type: image/{$type}");
|
||||
switch ($type) {
|
||||
case 'png':
|
||||
imagepng($this->im);
|
||||
break;
|
||||
case 'gif':
|
||||
imagegif($this->im);
|
||||
break;
|
||||
case 'jpeg':
|
||||
imagejpeg($this->im);
|
||||
break;
|
||||
case 'wbmp':
|
||||
imagewbmp($this->im);
|
||||
break;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回图像宽度
|
||||
* @return int 图像宽度
|
||||
@@ -257,16 +282,17 @@ class Image
|
||||
public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null)
|
||||
{
|
||||
//设置保存尺寸
|
||||
|
||||
empty($width) && $width = $w;
|
||||
empty($height) && $height = $h;
|
||||
do {
|
||||
//创建新图像
|
||||
$img = imagecreatetruecolor($width, $height);
|
||||
$img = imagecreatetruecolor((int) $width, (int) $height);
|
||||
// 调整默认颜色
|
||||
$color = imagecolorallocate($img, 255, 255, 255);
|
||||
imagefill($img, 0, 0, $color);
|
||||
//裁剪
|
||||
imagecopyresampled($img, $this->im, 0, 0, $x, $y, $width, $height, $w, $h);
|
||||
imagecopyresampled($img, $this->im, 0, 0, (int) $x, (int) $y, (int) $width, (int) $height, $w, $h);
|
||||
imagedestroy($this->im); //销毁原图
|
||||
//设置新图像
|
||||
$this->im = $img;
|
||||
@@ -302,18 +328,18 @@ class Image
|
||||
$scale = min($width / $w, $height / $h);
|
||||
//设置缩略图的坐标及宽度和高度
|
||||
$x = $y = 0;
|
||||
$width = $w * $scale;
|
||||
$height = $h * $scale;
|
||||
$width = (int) ($w * $scale);
|
||||
$height = (int) ($h * $scale);
|
||||
break;
|
||||
/* 居中裁剪 */
|
||||
case self::THUMB_CENTER:
|
||||
//计算缩放比例
|
||||
$scale = max($width / $w, $height / $h);
|
||||
//设置缩略图的坐标及宽度和高度
|
||||
$w = $width / $scale;
|
||||
$h = $height / $scale;
|
||||
$x = ($this->info['width'] - $w) / 2;
|
||||
$y = ($this->info['height'] - $h) / 2;
|
||||
$w = (int) ($width / $scale);
|
||||
$h = (int) ($height / $scale);
|
||||
$x = (int) (($this->info['width'] - $w) / 2);
|
||||
$y = (int) (($this->info['height'] - $h) / 2);
|
||||
break;
|
||||
/* 左上角裁剪 */
|
||||
case self::THUMB_NORTHWEST:
|
||||
@@ -321,18 +347,18 @@ class Image
|
||||
$scale = max($width / $w, $height / $h);
|
||||
//设置缩略图的坐标及宽度和高度
|
||||
$x = $y = 0;
|
||||
$w = $width / $scale;
|
||||
$h = $height / $scale;
|
||||
$w = (int) ($width / $scale);
|
||||
$h = (int) ($height / $scale);
|
||||
break;
|
||||
/* 右下角裁剪 */
|
||||
case self::THUMB_SOUTHEAST:
|
||||
//计算缩放比例
|
||||
$scale = max($width / $w, $height / $h);
|
||||
//设置缩略图的坐标及宽度和高度
|
||||
$w = $width / $scale;
|
||||
$h = $height / $scale;
|
||||
$x = $this->info['width'] - $w;
|
||||
$y = $this->info['height'] - $h;
|
||||
$w = (int) ($width / $scale);
|
||||
$h = (int) ($height / $scale);
|
||||
$x = (int) ($this->info['width'] - $w);
|
||||
$y = (int) ($this->info['height'] - $h);
|
||||
break;
|
||||
/* 填充 */
|
||||
case self::THUMB_FILLED:
|
||||
@@ -343,12 +369,12 @@ class Image
|
||||
$scale = min($width / $w, $height / $h);
|
||||
}
|
||||
//设置缩略图的坐标及宽度和高度
|
||||
$neww = $w * $scale;
|
||||
$newh = $h * $scale;
|
||||
$neww = (int) ($w * $scale);
|
||||
$newh = (int) ($h * $scale);
|
||||
$x = $this->info['width'] - $w;
|
||||
$y = $this->info['height'] - $h;
|
||||
$posx = ($width - $w * $scale) / 2;
|
||||
$posy = ($height - $h * $scale) / 2;
|
||||
$posx = (int) (($width - $w * $scale) / 2);
|
||||
$posy = (int) (($height - $h * $scale) / 2);
|
||||
do {
|
||||
//创建新图像
|
||||
$img = imagecreatetruecolor($width, $height);
|
||||
@@ -377,9 +403,9 @@ class Image
|
||||
/**
|
||||
* 添加水印
|
||||
*
|
||||
* @param string $source 水印图片路径
|
||||
* @param int $locate 水印位置
|
||||
* @param int $alpha 透明度
|
||||
* @param string $source 水印图片路径
|
||||
* @param int|array $locate 水印位置
|
||||
* @param int $alpha 透明度
|
||||
* @return $this
|
||||
*/
|
||||
public function water($source, $locate = self::WATER_SOUTHEAST, $alpha = 100)
|
||||
@@ -457,10 +483,10 @@ class Image
|
||||
// 调整默认颜色
|
||||
$color = imagecolorallocate($src, 255, 255, 255);
|
||||
imagefill($src, 0, 0, $color);
|
||||
imagecopy($src, $this->im, 0, 0, $x, $y, $info[0], $info[1]);
|
||||
imagecopy($src, $this->im, 0, 0, (int) $x, (int) $y, $info[0], $info[1]);
|
||||
imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]);
|
||||
imagecopymerge($this->im, $src, $x, $y, 0, 0, $info[0], $info[1], $alpha);
|
||||
//销毁零时图片资源
|
||||
imagecopymerge($this->im, $src, (int) $x, (int) $y, 0, 0, $info[0], $info[1], $alpha);
|
||||
//销毁临时图片资源
|
||||
imagedestroy($src);
|
||||
} while (!empty($this->gif) && $this->gifNext());
|
||||
//销毁水印资源
|
||||
@@ -471,13 +497,13 @@ class Image
|
||||
/**
|
||||
* 图像添加文字
|
||||
*
|
||||
* @param string $text 添加的文字
|
||||
* @param string $font 字体路径
|
||||
* @param integer $size 字号
|
||||
* @param string $color 文字颜色
|
||||
* @param int $locate 文字写入位置
|
||||
* @param integer $offset 文字相对当前位置的偏移量
|
||||
* @param integer $angle 文字倾斜角度
|
||||
* @param string $text 添加的文字
|
||||
* @param string $font 字体路径
|
||||
* @param integer $size 字号
|
||||
* @param string $color 文字颜色
|
||||
* @param int|array $locate 文字写入位置
|
||||
* @param integer|array $offset 文字相对当前位置的偏移量
|
||||
* @param integer $angle 文字倾斜角度
|
||||
*
|
||||
* @return $this
|
||||
* @throws ImageException
|
||||
@@ -559,6 +585,24 @@ class Image
|
||||
$offset = intval($offset);
|
||||
$ox = $oy = $offset;
|
||||
}
|
||||
/* 图片黑白检测 */
|
||||
if ("auto" == $color) {
|
||||
//X方向采集宽度:单英文字符占据宽度约为字体大小/1.6,单中文字符占据宽度约为字体大小*4/3;Y方向采集宽度:英文字符高度约为字体大小,中文会高一些。
|
||||
//使用保守宽度,以免在纯英文情况下采集区域超出图像范围,并且精度完全可以满足本功能。
|
||||
$pickX = intval(mb_strwidth($text) * ($size / 1.6));
|
||||
$pickY = $size;
|
||||
|
||||
$brightness = 0;
|
||||
for ($i = $x + $ox; $i < $pickX + $x + $ox; $i++) {
|
||||
//根据文字基线确定要进行遍历的像素
|
||||
for ($j = $y + $oy - $pickY; $j < $y + $oy; $j++) {
|
||||
//基线修正
|
||||
$brightness += self::getBrightnessOfPixel($i, $j);
|
||||
}
|
||||
}
|
||||
|
||||
$color = $brightness / ($pickX * $pickY) > 127 ? '#00000000' : '#ffffffff';
|
||||
}
|
||||
/* 设置颜色 */
|
||||
if (is_string($color) && 0 === strpos($color, '#')) {
|
||||
$color = str_split(substr($color, 1), 2);
|
||||
@@ -577,6 +621,22 @@ class Image
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片指定像素点的亮度值
|
||||
*/
|
||||
private function getBrightnessOfPixel($x, $y)
|
||||
{
|
||||
$rgb = imagecolorat($this->im, $x, $y);
|
||||
$r = ($rgb >> 16) & 0xFF;
|
||||
$g = ($rgb >> 8) & 0xFF;
|
||||
$b = $rgb & 0xFF;
|
||||
|
||||
//红绿蓝能量不同,亮度不同,对应系数也不同(参考https://www.w3.org/TR/AERT/#color-contrast)
|
||||
$brightness = intval($r * 0.299 + $g * 0.587 + $b * 0.114);
|
||||
|
||||
return $brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换到GIF的下一帧并保存当前帧
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
namespace think\image;
|
||||
|
||||
|
||||
class Exception extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
namespace think\image\gif;
|
||||
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public $GIF_buffer = [];
|
||||
@@ -204,4 +203,4 @@ class Decoder
|
||||
{
|
||||
return ($this->GIF_delays);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class Encoder
|
||||
for ($i = 0; $i < count($GIF_src); $i++) {
|
||||
if (strtolower($GIF_mod) == "url") {
|
||||
$this->BUF[] = fread(fopen($GIF_src[$i], "rb"), filesize($GIF_src[$i]));
|
||||
} else if (strtolower($GIF_mod) == "bin") {
|
||||
} elseif (strtolower($GIF_mod) == "bin") {
|
||||
$this->BUF[] = $GIF_src[$i];
|
||||
} else {
|
||||
printf("%s: %s ( %s )!", $this->VER, $this->ERR['ERR02'], $GIF_mod);
|
||||
@@ -74,7 +74,7 @@ class Encoder
|
||||
}
|
||||
$this->addHeader();
|
||||
for ($i = 0; $i < count($this->BUF); $i++) {
|
||||
$this->addFrames($i, $GIF_dly[$i]);
|
||||
isset($GIF_dly[$i]) && $this->addFrames($i, $GIF_dly[$i]);
|
||||
}
|
||||
$this->addFooter();
|
||||
}
|
||||
@@ -219,4 +219,4 @@ class Encoder
|
||||
{
|
||||
return ($this->GIF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,4 +85,4 @@ class Gif
|
||||
$gif = new Encoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
|
||||
file_put_contents($pathname, $gif->getAnimation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,4 +64,4 @@ class CropTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ class FlipTest extends TestCase
|
||||
@unlink($pathname);
|
||||
}
|
||||
|
||||
|
||||
public function testGif()
|
||||
{
|
||||
$pathname = TEST_PATH . 'tmp/flip.gif';
|
||||
@@ -40,4 +39,4 @@ class FlipTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ class InfoTest extends TestCase
|
||||
$this->assertEquals([800, 600], $image->size());
|
||||
}
|
||||
|
||||
|
||||
public function testPng()
|
||||
{
|
||||
$image = Image::open($this->getPng());
|
||||
@@ -57,4 +56,4 @@ class InfoTest extends TestCase
|
||||
$this->assertEquals('image/gif', $image->mime());
|
||||
$this->assertEquals([380, 216], $image->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,4 @@ class RotateTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,4 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
return new File(TEST_PATH . 'images/test.gif');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ class TextTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,6 @@ class ThumbTest extends TestCase
|
||||
@unlink($pathname);
|
||||
}
|
||||
|
||||
|
||||
public function testPng()
|
||||
{
|
||||
$pathname = TEST_PATH . 'tmp/thumb.png';
|
||||
@@ -281,4 +280,4 @@ class ThumbTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ class WaterTest extends TestCase
|
||||
|
||||
@unlink($pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ define('TEST_PATH', __DIR__ . '/');
|
||||
// 加载框架基础文件
|
||||
require __DIR__ . '/../thinkphp/base.php';
|
||||
\think\Loader::addNamespace('tests', TEST_PATH);
|
||||
\think\Loader::addNamespace('think', __DIR__ . '/../src/');
|
||||
\think\Loader::addNamespace('think', __DIR__ . '/../src/');
|
||||
|
||||
Reference in New Issue
Block a user