diff --git a/README.md b/README.md
index 3023f6e..c652cd0 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@ thinkphp8 blade view engine
+
@@ -23,20 +24,24 @@ composer require isszz/think-blade
// view.php 模板配置, 多应用时, 每个应用的配置可以不同
return [
- // 视图目录名
- 'dir_name' => 'view',
+ // 模板引擎类型使用Blade
+ 'type' => 'Blade',
// 模版主题
'theme' => '',
+ // 缓存路径
+ 'compiled' => '',
+ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+ 'auto_rule' => 1,
+ // 视图目录名
+ 'view_dir_name' => 'view',
// 模板起始路径
- 'base_path' => '',
- // 模板文件后缀
- 'suffix' => 'blade.php',
+ 'view_path' => '',
+ // 模板后缀
+ 'view_suffix' => 'html.php',
// 模板文件名分隔符
- 'depr' => DIRECTORY_SEPARATOR,
- // 缓存路径
- 'compiled' => '', // 默认留空使用runtime目录
- // 是否开启模板编译缓存, 设为false则每次都会重新编译
- 'cache' => true,
+ 'view_depr' => DIRECTORY_SEPARATOR,
+ // 是否开启模板编译缓存,设为false则每次都会重新编译
+ 'tpl_cache' => true,
];
```
@@ -55,9 +60,9 @@ return [
Blade 允许你使用 directive 方法自定义指令。当 Blade 编译器遇到自定义指令时,这会调用该指令包含的表达式提供的回调。
```php
-use think\blade\facade\Blade;
+use think\facade\View;
-Blade::directive('time2str', function($expression) {
+View::directive('time2str', function($expression) {
return "";
});
```
@@ -73,9 +78,9 @@ Blade::directive('time2str', function($expression) {
在定义简单的、自定义条件语句时,编写自定义指令比必须的步骤复杂。在这种情况下,think Blade 提供了 View::if 方法,它允许你使用闭包快速度定义条件指令。例如,定义一个校验当前应用的自定义指令
```php
-use think\blade\facade\Blade;
+use think\facade\View;
-Blade::if('app', function (...$apps) {
+View::if('app', function (...$apps) {
$appName = app('http')->getName();
if (count($apps) > 0) {
@@ -115,7 +120,7 @@ Blade::if('app', function (...$apps) {
### 中间件 挂载 auth 到 app 案例
```php
-use think\blade\facade\View;
+use think\facade\View;
/**
* 认证
@@ -162,7 +167,7 @@ class Auth
### 有条件地编译 class 样式
该`@class`指令有条件地编译 CSS class 样式。该指令接收一个数组,其中数组的键包含你希望添加的一个或多个样式的类名,而值是一个布尔表达式。如果数组元素有一个数值的键,它将始终包含在呈现的 class 列表中:
-```
+```html
// 多行php代码
@php
$isActive = false;
@@ -181,7 +186,7 @@ class Auth
```
### 同样,@style 指令可用于有条件地将内联 CSS 样式添加到一个 HTML 元素中。
-```
+```html
// 单行php代码可以简写如下
@php($isActive = true)
diff --git a/composer.json b/composer.json
index 15d2b34..ff12bc4 100644
--- a/composer.json
+++ b/composer.json
@@ -13,9 +13,6 @@
"ext-json": "*",
"ext-fileinfo": "*",
"ext-mbstring": "*",
- "ramsey/uuid": "^4.7",
- "doctrine/inflector": "^2.0",
- "symfony/finder": "^4.3.4",
"topthink/framework": "8.*"
},
"autoload": {
@@ -25,6 +22,7 @@
],
"psr-4": {
"think\\blade\\": "src",
+ "think\\view\\driver\\": "src",
"Illuminate\\Container\\": "src/Container",
"Illuminate\\Contracts\\": "src/Contracts",
"Illuminate\\Support\\": "src/Support",
@@ -37,11 +35,6 @@
},
"minimum-stability": "dev",
"extra": {
- "think": {
- "services": [
- "think\\blade\\BladeService"
- ]
- },
"branch-alias": {
"dev-master": "dev-dev"
}
diff --git a/src/Blade.php b/src/Blade.php
new file mode 100644
index 0000000..23cda80
--- /dev/null
+++ b/src/Blade.php
@@ -0,0 +1,316 @@
+ '',
+ // 缓存路径
+ 'compiled' => '',
+ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+ 'auto_rule' => 1,
+ // 视图目录名
+ 'view_dir_name' => 'view',
+ // 模板起始路径
+ 'view_path' => '',
+ // 模板后缀
+ 'view_suffix' => 'html.php',
+ // 模板文件名分隔符
+ 'view_depr' => DIRECTORY_SEPARATOR,
+ // 是否开启模板编译缓存,设为false则每次都会重新编译
+ 'tpl_cache' => true,
+ ];
+
+ public function __construct(App $app, array $config = [])
+ {
+ $this->app = $app;
+ $this->config = array_merge($this->config, (array) $config);
+
+ if (empty($this->config['compiled'])) {
+ $this->config['compiled'] = $app->getRuntimePath() . 'view' . DIRECTORY_SEPARATOR;
+ }
+
+ // 缓存主题路径
+ if (!empty($this->config['theme'])) {
+ $this->config['compiled'] .= $this->config['theme'] . DIRECTORY_SEPARATOR;
+ }
+
+ // debug 不缓存
+ if ($this->app->isDebug()) {
+ $this->config['tpl_cache'] = false;
+ }
+
+ if (empty($this->config['view_path'])) {
+ $path = $app->getAppPath() .'view'. DS;
+ } else {
+ $path = realpath($this->config['view_path']) . DS .'view'. DS;
+ }
+
+ $this->blade = (new BladeInstance(
+ $app,
+ $path,
+ $this->config['compiled'],
+ $this->config['tpl_cache'],
+ ))->getViewFactory();
+
+ $this->blade->addExtension($this->config['view_suffix'] ?: 'html.php', 'blade');
+ }
+
+ /**
+ * 检测是否存在模板文件
+ *
+ * @param string $template 模板文件或者模板规则
+ * @return bool
+ */
+ public function exists(string $template): bool
+ {
+ $template = $this->normalize($template);
+
+ if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+ $template = $this->parseTemplate($template);
+ }
+
+ return is_file($template);
+ }
+
+ /**
+ * 渲染模板文件
+ *
+ * @param string $template 模板文件
+ * @param array $data 模板变量
+ * @return void
+ */
+ public function fetch(string $template, array $data = []): void
+ {
+ $templatePath = '';
+
+ $template = $this->normalize($template);
+
+ if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+ $templatePath = $this->parseTemplate($template);
+ }
+
+ // 模板不存在 抛出异常
+ if (!$templatePath || !is_file($templatePath)) {
+
+ $app = $this->app->http->getName();
+ $controller = $this->app->request->controller();
+
+ $errorTemplate = $this->normalize($template, true);
+ if (strpos($template, '@') === false && strpos($template, '/') === false) {
+ $errorTemplate = $app .'@'. $controller .'.'. $errorTemplate;
+ }
+
+ throw new ViewNotFoundException(
+ 'View not exists: ' . $errorTemplate,
+ $templatePath,
+ $this->app->http->getName() .'@'. $this->app->request->controller(),
+ );
+
+ throw new ViewNotFoundException('View not exists:' . $this->normalize($template, true), $templatePath);
+ }
+
+ // 记录视图信息
+ $this->app['log']
+ ->record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]');
+
+ echo $this->blade->file($templatePath, $data)->render();
+ }
+
+ /**
+ * 渲染模板内容
+ *
+ * @param string $template 模板内容
+ * @param array $data 模板变量
+ * @return void
+ */
+ public function display(string $template, array $data = []): void
+ {
+ echo $this->fetch($template, $data);
+ }
+
+ /**
+ * 自动定位模板文件
+ *
+ * @param string $template 模板文件规则
+ * @return string
+ */
+ private function parseTemplate(string $template): string
+ {
+ $request = $this->app->request;
+
+ $app = null;
+ $depr = $this->config['view_depr'];
+ $view = $this->config['view_dir_name'] ?: 'view';
+
+ // 获取视图根目录
+ if (strpos($template, '@')) {
+ // 跨应用调用
+ [$app, $template] = explode('@', $template);
+ }
+
+ // 多应用模式
+ if(class_exists('\think\app\MultiApp')) {
+
+ $appName = is_null($app) ? $this->app->http->getName() : $app;
+
+ if (is_dir($this->app->getAppPath() . $view)) {
+ $path = (is_null($app) ? $this->app->getAppPath() : $this->app->getBasePath() . $appName). $depr . $view . $depr;
+ } else {
+ $path = $this->app->getRootPath() . $view . $depr . $appName . $depr;
+ }
+ } else {
+ // 单应用模式
+ $path = $this->app->getRootPath() . $view . $depr;
+ }
+
+ // 设置主题路径
+ if (!empty($this->config['theme'])) {
+ $path .= $this->config['theme'] . $depr;
+ }
+
+ if (0 !== strpos($template, '/')) {
+ $template = str_replace(['/', ':'], $depr, $template);
+ $controller = $request->controller();
+ if (strpos($controller, '.')) {
+ $pos = strrpos($controller, '.');
+ $controller = substr($controller, 0, $pos) . '.' . Str::snake(substr($controller, $pos + 1));
+ } else {
+ $controller = Str::snake($controller);
+ }
+
+ if ($controller) {
+ if ('' == $template) {
+ // 如果模板文件名为空 按照默认规则定位
+ if (2 == $this->config['auto_rule']) {
+ $template = $request->action(true);
+ } elseif (3 == $this->config['auto_rule']) {
+ $template = $request->action();
+ } else {
+ $template = Str::snake($request->action());
+ }
+
+ $template = str_replace('.', $depr, $controller) . $depr . $template;
+ } elseif (false === strpos($template, $depr)) {
+ $template = str_replace('.', $depr, $controller) . $depr . $template;
+ }
+ }
+ } else {
+ $template = str_replace(['/', ':'], $depr, substr($template, 1));
+ }
+
+ $template = $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.');
+
+ if (is_file($template)) {
+ return $template;
+ }
+
+ // 未设置主题, 尝试先去default查找
+ if(empty($this->config['theme'])) {
+ $default = str_replace(
+ $depr .'view'. $depr,
+ $depr .'view'. $depr .'default'. $depr,
+ $template
+ );
+
+ if (is_file($default)) {
+ return $default;
+ }
+ }
+
+ // 默认主题不存在模版, 降级删除default主题继续查找
+ if (strpos($template, $depr .'view'. $depr . 'default' . $depr) !== false) {
+ $default = str_replace(
+ $depr .'view'. $depr .'default'. $depr,
+ $depr .'view'. $depr,
+ $template
+ );
+
+ if (is_file($default)) {
+ return $default;
+ }
+ }
+
+ // 已设置主题, 但是找不到模版, 尝试降级为default主题
+ if (strpos($template, $depr .'view'. $depr . $this->config['theme'] . $depr) !== false) {
+ $default = str_replace(
+ $depr . $this->config['theme'] . $depr,
+ $depr .'default'. $depr,
+ $template
+ );
+
+ if (is_file($default)) {
+ return $default;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Normalize the given template.
+ *
+ * @param string $name
+ * @return string
+ */
+ private function normalize($template = '', $isRaw = false)
+ {
+ if($isRaw && strpos($template, '/')) {
+ return str_replace('/', '.', $template);
+ }
+
+ if (strpos($template, '.')) {
+ $template = str_replace('.', '/', $template);
+ }
+
+ return $template;
+ }
+
+ /**
+ * 配置模板引擎
+ *
+ * @param array $config 参数
+ * @return void
+ */
+ public function config(array $config): void
+ {
+ $this->config = array_merge($this->config, $config);
+ }
+
+ /**
+ * 获取模板引擎配置
+ *
+ * @param string $name 参数名
+ * @return mixed
+ */
+ public function getConfig(string $name)
+ {
+ return $this->config[$name];
+ }
+
+ public function __debugInfo()
+ {
+ return [
+ 'config' => $this->config,
+ 'blade' => $this->blade
+ ];
+ }
+
+ public function __call($method, $params)
+ {
+ return call_user_func_array([$this->blade, $method], $params);
+ }
+}
diff --git a/src/BladeService.php b/src/BladeInstance.php
similarity index 71%
rename from src/BladeService.php
rename to src/BladeInstance.php
index e01a0b8..2daa084 100644
--- a/src/BladeService.php
+++ b/src/BladeInstance.php
@@ -1,84 +1,64 @@
config = $config->get('view');
-
- if (empty($this->config['compiled'])) {
- $this->config['compiled'] = $this->app->getRuntimePath(); // . 'view' . DS
- }
-
- $this->compiled = $this->config['compiled'];
- $this->isCache = $this->config['cache'] ?? false;
- $this->compiledExtension = $this->config['compiled_extension'] ?? 'php';*/
-
- $this->compiled = $this->app->getRuntimePath();
-
- // if(class_exists('\think\app\MultiApp')) {}
-
- if (is_null($this->files)) {
- $this->files = new Filesystem;
- }
+ $this->app = $app;
+ $this->path = $path;
+ $this->cachePath = $cachePath;
+ $this->isCache = $isCache;
$this->registerFactory();
- $this->registerViewFinder();
+ // $this->registerViewFinder();
$this->registerBladeCompiler();
$this->registerEngineResolver();
+
+ return $this;
}
/**
@@ -91,7 +71,8 @@ public function registerFactory()
$this->app->bind('blade.view', function () {
$factory = $this->createFactory(
$this->app->get('view.engine.resolver'),
- $this->app->get('view.finder')
+ null,
+ // $this->app->get('view.finder')
);
// We will also set the container instance on this view environment since the
@@ -114,7 +95,7 @@ public function registerFactory()
*/
protected function createFactory($resolver, $finder)
{
- return new Factory($resolver, $finder, $this->app);
+ return new Factory($this->app, $resolver, $finder);
}
/**
@@ -125,8 +106,8 @@ protected function createFactory($resolver, $finder)
public function registerViewFinder()
{
$this->app->bind('view.finder', function () {
- return new FileViewFinder($this->files, [
- $this->compiled,
+ return new FileViewFinder([
+ $this->path,
]);
});
}
@@ -140,9 +121,8 @@ public function registerBladeCompiler()
{
$this->app->bind('blade.compiler', function () {
return tap(new BladeCompiler(
- $this->files,
- // $this->compiled,
- // $this->isCache,
+ $this->cachePath,
+ $this->isCache,
// $this->compiledExtension,
), function ($blade) {
$blade->component('dynamic-component', \Illuminate\View\DynamicComponent::class);
@@ -180,7 +160,7 @@ public function registerEngineResolver()
public function registerFileEngine($resolver)
{
$resolver->register('file', function () {
- return new FileEngine($this->files);
+ return new FileEngine();
});
}
@@ -193,7 +173,7 @@ public function registerFileEngine($resolver)
public function registerPhpEngine($resolver)
{
$resolver->register('php', function () {
- return new PhpEngine($this->files);
+ return new PhpEngine();
});
}
@@ -208,11 +188,26 @@ public function registerBladeEngine($resolver)
$resolver->register('blade', function () {
$compiler = new CompilerEngine($this->app->get('blade.compiler'));
- Container::getInstance()->resolving(function() use ($compiler) {
+ $this->app->resolving(function() use ($compiler) {
$compiler->forgetCompiledOrNotExpired();
});
return $compiler;
});
}
+
+ /**
+ * Get the laravel view factory.
+ *
+ * @return Factory
+ */
+ public function getViewFactory(): Factory
+ {
+ return $this->app->get('blade.view');
+ }
+
+ public function __call($method, $params)
+ {
+ return call_user_func_array([$this->app->get('blade.compiler'), $method], $params);
+ }
}
diff --git a/src/Contracts/Filesystem/Cloud.php b/src/Contracts/Filesystem/Cloud.php
deleted file mode 100644
index 86bea26..0000000
--- a/src/Contracts/Filesystem/Cloud.php
+++ /dev/null
@@ -1,14 +0,0 @@
-exists($path);
- }
-
- /**
- * Get the contents of a file.
- *
- * @param string $path
- * @param bool $lock
- * @return string
- *
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
- */
- public function get($path, $lock = false)
- {
- if ($this->isFile($path)) {
- return $lock ? $this->sharedGet($path) : file_get_contents($path);
- }
-
- throw new FileNotFoundException("File does not exist at path {$path}");
- }
-
- /**
- * Get contents of a file with shared access.
- *
- * @param string $path
- * @return string
- */
- public function sharedGet($path)
- {
- $contents = '';
-
- $handle = fopen($path, 'rb');
-
- if ($handle) {
- try {
- if (flock($handle, LOCK_SH)) {
- clearstatcache(true, $path);
-
- $contents = fread($handle, $this->size($path) ?: 1);
-
- flock($handle, LOCK_UN);
- }
- } finally {
- fclose($handle);
- }
- }
-
- return $contents;
- }
-
- /**
- * Get the returned value of a file.
- *
- * @param string $path
- * @param array $data
- * @return mixed
- *
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
- */
- public function getRequire($path, array $data = [])
- {
- if ($this->isFile($path)) {
- $__path = $path;
- $__data = $data;
-
- return (static function () use ($__path, $__data) {
- extract($__data, EXTR_SKIP);
-
- return require $__path;
- })();
- }
-
- throw new FileNotFoundException("File does not exist at path {$path}.");
- }
-
- /**
- * Require the given file once.
- *
- * @param string $path
- * @param array $data
- * @return mixed
- *
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
- */
- public function requireOnce($path, array $data = [])
- {
- if ($this->isFile($path)) {
- $__path = $path;
- $__data = $data;
-
- return (static function () use ($__path, $__data) {
- extract($__data, EXTR_SKIP);
-
- return require_once $__path;
- })();
- }
-
- throw new FileNotFoundException("File does not exist at path {$path}.");
- }
-
- /**
- * Get the hash of the file at the given path.
- *
- * @param string $path
- * @param string $algorithm
- * @return string
- */
- public function hash($path, $algorithm = 'md5')
- {
- return hash_file($algorithm, $path);
- }
-
- /**
- * Write the contents of a file.
- *
- * @param string $path
- * @param string $contents
- * @param bool $lock
- * @return int|bool
- */
- public function put($path, $contents, $lock = false)
- {
- return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
- }
-
- /**
- * Write the contents of a file, replacing it atomically if it already exists.
- *
- * @param string $path
- * @param string $content
- * @return void
- */
- public function replace($path, $content)
- {
- // If the path already exists and is a symlink, get the real path...
- clearstatcache(true, $path);
-
- $path = realpath($path) ?: $path;
-
- $tempPath = tempnam(dirname($path), basename($path));
-
- // Fix permissions of tempPath because `tempnam()` creates it with permissions set to 0600...
- chmod($tempPath, 0777 - umask());
-
- file_put_contents($tempPath, $content);
-
- rename($tempPath, $path);
- }
-
- /**
- * Replace a given string within a given file.
- *
- * @param array|string $search
- * @param array|string $replace
- * @param string $path
- * @return void
- */
- public function replaceInFile($search, $replace, $path)
- {
- file_put_contents($path, str_replace($search, $replace, file_get_contents($path)));
- }
-
- /**
- * Prepend to a file.
- *
- * @param string $path
- * @param string $data
- * @return int
- */
- public function prepend($path, $data)
- {
- if ($this->exists($path)) {
- return $this->put($path, $data.$this->get($path));
- }
-
- return $this->put($path, $data);
- }
-
- /**
- * Append to a file.
- *
- * @param string $path
- * @param string $data
- * @return int
- */
- public function append($path, $data)
- {
- return file_put_contents($path, $data, FILE_APPEND);
- }
-
- /**
- * Get or set UNIX mode of a file or directory.
- *
- * @param string $path
- * @param int|null $mode
- * @return mixed
- */
- public function chmod($path, $mode = null)
- {
- if ($mode) {
- return chmod($path, $mode);
- }
-
- return substr(sprintf('%o', fileperms($path)), -4);
- }
-
- /**
- * Delete the file at a given path.
- *
- * @param string|array $paths
- * @return bool
- */
- public function delete($paths)
- {
- $paths = is_array($paths) ? $paths : func_get_args();
-
- $success = true;
-
- foreach ($paths as $path) {
- try {
- if (! @unlink($path)) {
- $success = false;
- }
- } catch (ErrorException $e) {
- $success = false;
- }
- }
-
- return $success;
- }
-
- /**
- * Move a file to a new location.
- *
- * @param string $path
- * @param string $target
- * @return bool
- */
- public function move($path, $target)
- {
- return rename($path, $target);
- }
-
- /**
- * Copy a file to a new location.
- *
- * @param string $path
- * @param string $target
- * @return bool
- */
- public function copy($path, $target)
- {
- return copy($path, $target);
- }
-
- /**
- * Create a hard link to the target file or directory.
- *
- * @param string $target
- * @param string $link
- * @return void
- */
- public function link($target, $link)
- {
- if (! windows_os()) {
- return symlink($target, $link);
- }
-
- $mode = $this->isDirectory($target) ? 'J' : 'H';
-
- exec("mklink /{$mode} ".escapeshellarg($link).' '.escapeshellarg($target));
- }
-
- /**
- * Extract the file name from a file path.
- *
- * @param string $path
- * @return string
- */
- public function name($path)
- {
- return pathinfo($path, PATHINFO_FILENAME);
- }
-
- /**
- * Extract the trailing name component from a file path.
- *
- * @param string $path
- * @return string
- */
- public function basename($path)
- {
- return pathinfo($path, PATHINFO_BASENAME);
- }
-
- /**
- * Extract the parent directory from a file path.
- *
- * @param string $path
- * @return string
- */
- public function dirname($path)
- {
- return pathinfo($path, PATHINFO_DIRNAME);
- }
-
- /**
- * Extract the file extension from a file path.
- *
- * @param string $path
- * @return string
- */
- public function extension($path)
- {
- return pathinfo($path, PATHINFO_EXTENSION);
- }
-
- /**
- * Get the file type of a given file.
- *
- * @param string $path
- * @return string
- */
- public function type($path)
- {
- return filetype($path);
- }
-
- /**
- * Get the mime-type of a given file.
- *
- * @param string $path
- * @return string|false
- */
- public function mimeType($path)
- {
- return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
- }
-
- /**
- * Get the file size of a given file.
- *
- * @param string $path
- * @return int
- */
- public function size($path)
- {
- return filesize($path);
- }
-
- /**
- * Get the file's last modification time.
- *
- * @param string $path
- * @return int
- */
- public function lastModified($path)
- {
- return filemtime($path);
- }
-
- /**
- * Determine if the given path is a directory.
- *
- * @param string $directory
- * @return bool
- */
- public function isDirectory($directory)
- {
- return is_dir($directory);
- }
-
- /**
- * Determine if the given path is readable.
- *
- * @param string $path
- * @return bool
- */
- public function isReadable($path)
- {
- return is_readable($path);
- }
-
- /**
- * Determine if the given path is writable.
- *
- * @param string $path
- * @return bool
- */
- public function isWritable($path)
- {
- return is_writable($path);
- }
-
- /**
- * Determine if two files are the same by comparing their hashes.
- *
- * @param string $firstFile
- * @param string $secondFile
- * @return bool
- */
- public function hasSameHash($firstFile, $secondFile)
- {
- $hash = @md5_file($firstFile);
-
- return $hash && $hash === @md5_file($secondFile);
- }
-
- /**
- * Determine if the given path is a file.
- *
- * @param string $file
- * @return bool
- */
- public function isFile($file)
- {
- return is_file($file);
- }
-
- /**
- * Find path names matching a given pattern.
- *
- * @param string $pattern
- * @param int $flags
- * @return array
- */
- public function glob($pattern, $flags = 0)
- {
- return glob($pattern, $flags);
- }
-
- /**
- * Get an array of all files in a directory.
- *
- * @param string $directory
- * @param bool $hidden
- * @return \Symfony\Component\Finder\SplFileInfo[]
- */
- public function files($directory, $hidden = false)
- {
- return iterator_to_array(
- Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->depth(0)->sortByName(),
- false
- );
- }
-
- /**
- * Get all of the files from the given directory (recursive).
- *
- * @param string $directory
- * @param bool $hidden
- * @return \Symfony\Component\Finder\SplFileInfo[]
- */
- public function allFiles($directory, $hidden = false)
- {
- return iterator_to_array(
- Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->sortByName(),
- false
- );
- }
-
- /**
- * Get all of the directories within a given directory.
- *
- * @param string $directory
- * @return array
- */
- public function directories($directory)
- {
- $directories = [];
-
- foreach (Finder::create()->in($directory)->directories()->depth(0)->sortByName() as $dir) {
- $directories[] = $dir->getPathname();
- }
-
- return $directories;
- }
-
- /**
- * Create a directory.
- *
- * @param string $path
- * @param int $mode
- * @param bool $recursive
- * @param bool $force
- * @return bool
- */
- public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false)
- {
- if ($force) {
- return @mkdir($path, $mode, $recursive);
- }
-
- return mkdir($path, $mode, $recursive);
- }
-
- /**
- * Move a directory.
- *
- * @param string $from
- * @param string $to
- * @param bool $overwrite
- * @return bool
- */
- public function moveDirectory($from, $to, $overwrite = false)
- {
- if ($overwrite && $this->isDirectory($to) && ! $this->deleteDirectory($to)) {
- return false;
- }
-
- return @rename($from, $to) === true;
- }
-
- /**
- * Copy a directory from one location to another.
- *
- * @param string $directory
- * @param string $destination
- * @param int|null $options
- * @return bool
- */
- public function copyDirectory($directory, $destination, $options = null)
- {
- if (! $this->isDirectory($directory)) {
- return false;
- }
-
- $options = $options ?: FilesystemIterator::SKIP_DOTS;
-
- // If the destination directory does not actually exist, we will go ahead and
- // create it recursively, which just gets the destination prepared to copy
- // the files over. Once we make the directory we'll proceed the copying.
- if (! $this->isDirectory($destination)) {
- $this->makeDirectory($destination, 0777, true);
- }
-
- $items = new FilesystemIterator($directory, $options);
-
- foreach ($items as $item) {
- // As we spin through items, we will check to see if the current file is actually
- // a directory or a file. When it is actually a directory we will need to call
- // back into this function recursively to keep copying these nested folders.
- $target = $destination.'/'.$item->getBasename();
-
- if ($item->isDir()) {
- $path = $item->getPathname();
-
- if (! $this->copyDirectory($path, $target, $options)) {
- return false;
- }
- }
-
- // If the current items is just a regular file, we will just copy this to the new
- // location and keep looping. If for some reason the copy fails we'll bail out
- // and return false, so the developer is aware that the copy process failed.
- else {
- if (! $this->copy($item->getPathname(), $target)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Recursively delete a directory.
- *
- * The directory itself may be optionally preserved.
- *
- * @param string $directory
- * @param bool $preserve
- * @return bool
- */
- public function deleteDirectory($directory, $preserve = false)
- {
- if (! $this->isDirectory($directory)) {
- return false;
- }
-
- $items = new FilesystemIterator($directory);
-
- foreach ($items as $item) {
- // If the item is a directory, we can just recurse into the function and
- // delete that sub-directory otherwise we'll just delete the file and
- // keep iterating through each file until the directory is cleaned.
- if ($item->isDir() && ! $item->isLink()) {
- $this->deleteDirectory($item->getPathname());
- }
-
- // If the item is just a file, we can go ahead and delete it since we're
- // just looping through and waxing all of the files in this directory
- // and calling directories recursively, so we delete the real path.
- else {
- $this->delete($item->getPathname());
- }
- }
-
- if (! $preserve) {
- @rmdir($directory);
- }
-
- return true;
- }
-
- /**
- * Remove all of the directories within a given directory.
- *
- * @param string $directory
- * @return bool
- */
- public function deleteDirectories($directory)
- {
- $allDirectories = $this->directories($directory);
-
- if (! empty($allDirectories)) {
- foreach ($allDirectories as $directoryName) {
- $this->deleteDirectory($directoryName);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Empty the specified directory of all files and folders.
- *
- * @param string $directory
- * @return bool
- */
- public function cleanDirectory($directory)
- {
- return $this->deleteDirectory($directory, true);
- }
-}
diff --git a/src/Support/Arr.php b/src/Support/Arr.php
index 13905d8..e18ea6e 100644
--- a/src/Support/Arr.php
+++ b/src/Support/Arr.php
@@ -1,7 +1,9 @@
$value) {
+ static::set($results, $key, $value);
+ }
+
+ return $results;
+ }
+
/**
* Get all of the given array except for a specified array of keys.
*
* @param array $array
- * @param array|string $keys
+ * @param array|string|int|float $keys
* @return array
*/
public static function except($array, $keys)
@@ -144,17 +163,25 @@ public static function except($array, $keys)
*/
public static function exists($array, $key)
{
+ if ($array instanceof Enumerable) {
+ return $array->has($key);
+ }
+
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
+ if (is_float($key)) {
+ $key = (string) $key;
+ }
+
return array_key_exists($key, $array);
}
/**
* Return the first element in an array passing a given truth test.
*
- * @param array $array
+ * @param iterable $array
* @param callable|null $callback
* @param mixed $default
* @return mixed
@@ -200,7 +227,7 @@ public static function last($array, callable $callback = null, $default = null)
/**
* Flatten a multi-dimensional array into a single level.
*
- * @param array $array
+ * @param iterable $array
* @param int $depth
* @return array
*/
@@ -231,7 +258,7 @@ public static function flatten($array, $depth = INF)
* Remove one or many array items from a given array using "dot" notation.
*
* @param array $array
- * @param array|string $keys
+ * @param array|string|int|float $keys
* @return void
*/
public static function forget(&$array, $keys)
@@ -260,7 +287,7 @@ public static function forget(&$array, $keys)
while (count($parts) > 1) {
$part = array_shift($parts);
- if (isset($array[$part]) && is_array($array[$part])) {
+ if (isset($array[$part]) && static::accessible($array[$part])) {
$array = &$array[$part];
} else {
continue 2;
@@ -275,8 +302,8 @@ public static function forget(&$array, $keys)
* Get an item from an array using "dot" notation.
*
* @param \ArrayAccess|array $array
- * @param string|int $key
- * @param mixed $default
+ * @param string|int|null $key
+ * @param mixed $default
* @return mixed
*/
public static function get($array, $key, $default = null)
@@ -293,7 +320,7 @@ public static function get($array, $key, $default = null)
return $array[$key];
}
- if (strpos($key, '.') === false) {
+ if (! str_contains($key, '.')) {
return $array[$key] ?? value($default);
}
@@ -342,6 +369,38 @@ public static function has($array, $keys)
return true;
}
+ /**
+ * Determine if any of the keys exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ * @return bool
+ */
+ public static function hasAny($array, $keys)
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+
+ $keys = (array) $keys;
+
+ if (! $array) {
+ return false;
+ }
+
+ if ($keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ if (static::has($array, $key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Determines if an array is associative.
*
@@ -357,6 +416,72 @@ public static function isAssoc(array $array)
return array_keys($keys) !== $keys;
}
+ /**
+ * Determines if an array is a list.
+ *
+ * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
+ *
+ * @param array $array
+ * @return bool
+ */
+ public static function isList($array)
+ {
+ return ! self::isAssoc($array);
+ }
+
+ /**
+ * Join all items using a string. The final items can use a separate glue string.
+ *
+ * @param array $array
+ * @param string $glue
+ * @param string $finalGlue
+ * @return string
+ */
+ public static function join($array, $glue, $finalGlue = '')
+ {
+ if ($finalGlue === '') {
+ return implode($glue, $array);
+ }
+
+ if (count($array) === 0) {
+ return '';
+ }
+
+ if (count($array) === 1) {
+ return end($array);
+ }
+
+ $finalItem = array_pop($array);
+
+ return implode($glue, $array).$finalGlue.$finalItem;
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param array $array
+ * @param callable|array|string $keyBy
+ * @return array
+ */
+ public static function keyBy($array, $keyBy)
+ {
+ return Collection::make($array)->keyBy($keyBy)->all();
+ }
+
+ /**
+ * Prepend the key names of an associative array.
+ *
+ * @param array $array
+ * @param string $prependWith
+ * @return array
+ */
+ public static function prependKeysWith($array, $prependWith)
+ {
+ return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) {
+ return [$prependWith.$key => $item];
+ })->all();
+ }
+
/**
* Get a subset of the items from the given array.
*
@@ -372,8 +497,8 @@ public static function only($array, $keys)
/**
* Pluck an array of values from an array.
*
- * @param array $array
- * @param string|array $value
+ * @param iterable $array
+ * @param string|array|int|null $value
* @param string|array|null $key
* @return array
*/
@@ -421,6 +546,26 @@ protected static function explodePluckParameters($value, $key)
return [$value, $key];
}
+ /**
+ * Run a map over each of the items in the array.
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return array
+ */
+ public static function map(array $array, callable $callback)
+ {
+ $keys = array_keys($array);
+
+ try {
+ $items = array_map($callback, $array, $keys);
+ } catch (ArgumentCountError) {
+ $items = array_map($callback, $array);
+ }
+
+ return array_combine($keys, $items);
+ }
+
/**
* Push an item onto the beginning of an array.
*
@@ -431,7 +576,7 @@ protected static function explodePluckParameters($value, $key)
*/
public static function prepend($array, $value, $key = null)
{
- if (is_null($key)) {
+ if (func_num_args() == 2) {
array_unshift($array, $value);
} else {
$array = [$key => $value] + $array;
@@ -443,9 +588,9 @@ public static function prepend($array, $value, $key = null)
/**
* Get a value from the array, and remove it.
*
- * @param array $array
- * @param string $key
- * @param mixed $default
+ * @param array $array
+ * @param string|int $key
+ * @param mixed $default
* @return mixed
*/
public static function pull(&$array, $key, $default = null)
@@ -457,16 +602,28 @@ public static function pull(&$array, $key, $default = null)
return $value;
}
+ /**
+ * Convert the array into a query string.
+ *
+ * @param array $array
+ * @return string
+ */
+ public static function query($array)
+ {
+ return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+ }
+
/**
* Get one or a specified number of random values from an array.
*
* @param array $array
* @param int|null $number
+ * @param bool|false $preserveKeys
* @return mixed
*
* @throws \InvalidArgumentException
*/
- public static function random($array, $number = null)
+ public static function random($array, $number = null, $preserveKeys = false)
{
$requested = is_null($number) ? 1 : $number;
@@ -490,8 +647,14 @@ public static function random($array, $number = null)
$results = [];
- foreach ((array) $keys as $key) {
- $results[] = $array[$key];
+ if ($preserveKeys) {
+ foreach ((array) $keys as $key) {
+ $results[$key] = $array[$key];
+ }
+ } else {
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
}
return $results;
@@ -502,9 +665,9 @@ public static function random($array, $number = null)
*
* If no key is given to the method, the entire array will be replaced.
*
- * @param array $array
- * @param string $key
- * @param mixed $value
+ * @param array $array
+ * @param string|int|null $key
+ * @param mixed $value
* @return array
*/
public static function set(&$array, $key, $value)
@@ -515,8 +678,12 @@ public static function set(&$array, $key, $value)
$keys = explode('.', $key);
- while (count($keys) > 1) {
- $key = array_shift($keys);
+ foreach ($keys as $i => $key) {
+ if (count($keys) === 1) {
+ break;
+ }
+
+ unset($keys[$i]);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
@@ -557,7 +724,7 @@ public static function shuffle($array, $seed = null)
* Sort the array using the given callback or "dot" notation.
*
* @param array $array
- * @param callable|string|null $callback
+ * @param callable|array|string|null $callback
* @return array
*/
public static function sort($array, $callback = null)
@@ -569,36 +736,31 @@ public static function sort($array, $callback = null)
* Recursively sort an array by keys and values.
*
* @param array $array
+ * @param int $options
+ * @param bool $descending
* @return array
*/
- public static function sortRecursive($array)
+ public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
{
foreach ($array as &$value) {
if (is_array($value)) {
- $value = static::sortRecursive($value);
+ $value = static::sortRecursive($value, $options, $descending);
}
}
if (static::isAssoc($array)) {
- ksort($array);
+ $descending
+ ? krsort($array, $options)
+ : ksort($array, $options);
} else {
- sort($array);
+ $descending
+ ? rsort($array, $options)
+ : sort($array, $options);
}
return $array;
}
- /**
- * Convert the array into a query string.
- *
- * @param array $array
- * @return string
- */
- public static function query($array)
- {
- return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
- }
-
/**
* Conditionally compile classes from an array into a CSS class list.
*
@@ -622,29 +784,6 @@ public static function toCssClasses($array)
return implode(' ', $classes);
}
- /**
- * Conditionally compile styles from an array into a style list.
- *
- * @param array $array
- * @return string
- */
- public static function toCssStyles($array)
- {
- $styleList = static::wrap($array);
-
- $styles = [];
-
- foreach ($styleList as $class => $constraint) {
- if (is_numeric($class)) {
- $styles[] = Str::finish($constraint, ';');
- } elseif ($constraint) {
- $styles[] = Str::finish($class, ';');
- }
- }
-
- return implode(' ', $styles);
- }
-
/**
* Filter the array using the given callback.
*
@@ -665,7 +804,9 @@ public static function where($array, callable $callback)
*/
public static function whereNotNull($array)
{
- return static::where($array, fn ($value) => ! is_null($value));
+ return static::where($array, function ($value) {
+ return ! is_null($value);
+ });
}
/**
diff --git a/src/Support/Collection.php b/src/Support/Collection.php
index f3c6526..467ce39 100644
--- a/src/Support/Collection.php
+++ b/src/Support/Collection.php
@@ -1,28 +1,41 @@
+ * @implements \Illuminate\Support\Enumerable
+ */
+class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
{
+ /**
+ * @use \Illuminate\Support\Traits\EnumeratesValues
+ */
use EnumeratesValues, Macroable;
/**
* The items contained in the collection.
*
- * @var array
+ * @var array
*/
protected $items = [];
/**
* Create a new collection.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items
* @return void
*/
public function __construct($items = [])
@@ -31,29 +44,21 @@ public function __construct($items = [])
}
/**
- * Create a new collection by invoking the callback a given amount of times.
+ * Create a collection with the given range.
*
- * @param int $number
- * @param callable $callback
- * @return static
+ * @param int $from
+ * @param int $to
+ * @return static
*/
- public static function times($number, callable $callback = null)
+ public static function range($from, $to)
{
- if ($number < 1) {
- return new static;
- }
-
- if (is_null($callback)) {
- return new static(range(1, $number));
- }
-
- return (new static(range(1, $number)))->map($callback);
+ return new static(range($from, $to));
}
/**
* Get all of the items in the collection.
*
- * @return array
+ * @return array
*/
public function all()
{
@@ -63,7 +68,7 @@ public function all()
/**
* Get a lazy collection for the items in this collection.
*
- * @return \Illuminate\Support\LazyCollection
+ * @return \Illuminate\Support\LazyCollection
*/
public function lazy()
{
@@ -73,8 +78,8 @@ public function lazy()
/**
* Get the average value of a given key.
*
- * @param callable|string|null $callback
- * @return mixed
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return float|int|null
*/
public function avg($callback = null)
{
@@ -94,15 +99,14 @@ public function avg($callback = null)
/**
* Get the median of a given key.
*
- * @param string|array|null $key
- * @return mixed
+ * @param string|array|null $key
+ * @return float|int|null
*/
public function median($key = null)
{
$values = (isset($key) ? $this->pluck($key) : $this)
- ->filter(function ($item) {
- return ! is_null($item);
- })->sort()->values();
+ ->filter(fn ($item) => ! is_null($item))
+ ->sort()->values();
$count = $values->count();
@@ -124,8 +128,8 @@ public function median($key = null)
/**
* Get the mode of a given key.
*
- * @param string|array|null $key
- * @return array|null
+ * @param string|array|null $key
+ * @return array|null
*/
public function mode($key = null)
{
@@ -135,25 +139,22 @@ public function mode($key = null)
$collection = isset($key) ? $this->pluck($key) : $this;
- $counts = new self;
+ $counts = new static;
- $collection->each(function ($value) use ($counts) {
- $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
- });
+ $collection->each(fn ($value) => $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1);
$sorted = $counts->sort();
$highestValue = $sorted->last();
- return $sorted->filter(function ($value) use ($highestValue) {
- return $value == $highestValue;
- })->sort()->keys()->all();
+ return $sorted->filter(fn ($value) => $value == $highestValue)
+ ->sort()->keys()->all();
}
/**
* Collapse the collection of items into a single array.
*
- * @return static
+ * @return static
*/
public function collapse()
{
@@ -163,7 +164,7 @@ public function collapse()
/**
* Determine if an item exists in the collection.
*
- * @param mixed $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -183,11 +184,27 @@ public function contains($key, $operator = null, $value = null)
return $this->contains($this->operatorForWhere(...func_get_args()));
}
+ /**
+ * Determine if an item is not contained in the collection.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function doesntContain($key, $operator = null, $value = null)
+ {
+ return ! $this->contains(...func_get_args());
+ }
+
/**
* Cross join with the given lists, returning all possible permutations.
*
- * @param mixed ...$lists
- * @return static
+ * @template TCrossJoinKey
+ * @template TCrossJoinValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists
+ * @return static>
*/
public function crossJoin(...$lists)
{
@@ -199,7 +216,7 @@ public function crossJoin(...$lists)
/**
* Get the items in the collection that are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diff($items)
@@ -208,10 +225,10 @@ public function diff($items)
}
/**
- * Get the items in the collection that are not present in the given items.
+ * Get the items in the collection that are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TValue, TValue): int $callback
* @return static
*/
public function diffUsing($items, callable $callback)
@@ -222,7 +239,7 @@ public function diffUsing($items, callable $callback)
/**
* Get the items in the collection whose keys and values are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diffAssoc($items)
@@ -231,10 +248,10 @@ public function diffAssoc($items)
}
/**
- * Get the items in the collection whose keys and values are not present in the given items.
+ * Get the items in the collection whose keys and values are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
* @return static
*/
public function diffAssocUsing($items, callable $callback)
@@ -245,7 +262,7 @@ public function diffAssocUsing($items, callable $callback)
/**
* Get the items in the collection whose keys are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diffKeys($items)
@@ -254,10 +271,10 @@ public function diffKeys($items)
}
/**
- * Get the items in the collection whose keys are not present in the given items.
+ * Get the items in the collection whose keys are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
* @return static
*/
public function diffKeysUsing($items, callable $callback)
@@ -268,7 +285,7 @@ public function diffKeysUsing($items, callable $callback)
/**
* Retrieve duplicate items from the collection.
*
- * @param callable|null $callback
+ * @param (callable(TValue): bool)|string|null $callback
* @param bool $strict
* @return static
*/
@@ -296,7 +313,7 @@ public function duplicates($callback = null, $strict = false)
/**
* Retrieve duplicate items from the collection using strict comparison.
*
- * @param callable|null $callback
+ * @param (callable(TValue): bool)|string|null $callback
* @return static
*/
public function duplicatesStrict($callback = null)
@@ -308,7 +325,7 @@ public function duplicatesStrict($callback = null)
* Get the comparison function to detect duplicates.
*
* @param bool $strict
- * @return \Closure
+ * @return callable(TValue, TValue): bool
*/
protected function duplicateComparator($strict)
{
@@ -326,7 +343,7 @@ protected function duplicateComparator($strict)
/**
* Get all items except for those with the specified keys.
*
- * @param \Illuminate\Support\Collection|mixed $keys
+ * @param \Illuminate\Support\Enumerable|array $keys
* @return static
*/
public function except($keys)
@@ -343,7 +360,7 @@ public function except($keys)
/**
* Run a filter over each of the items.
*
- * @param callable|null $callback
+ * @param (callable(TValue, TKey): bool)|null $callback
* @return static
*/
public function filter(callable $callback = null)
@@ -358,9 +375,11 @@ public function filter(callable $callback = null)
/**
* Get the first item from the collection passing the given truth test.
*
- * @param callable|null $callback
- * @param mixed $default
- * @return mixed
+ * @template TFirstDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TFirstDefault|(\Closure(): TFirstDefault) $default
+ * @return TValue|TFirstDefault
*/
public function first(callable $callback = null, $default = null)
{
@@ -371,7 +390,7 @@ public function first(callable $callback = null, $default = null)
* Get a flattened array of the items in the collection.
*
* @param int $depth
- * @return static
+ * @return static
*/
public function flatten($depth = INF)
{
@@ -381,7 +400,7 @@ public function flatten($depth = INF)
/**
* Flip the items in the collection.
*
- * @return static
+ * @return static
*/
public function flip()
{
@@ -391,7 +410,7 @@ public function flip()
/**
* Remove an item from the collection by key.
*
- * @param string|array $keys
+ * @param TKey|array $keys
* @return $this
*/
public function forget($keys)
@@ -406,29 +425,49 @@ public function forget($keys)
/**
* Get an item from the collection by key.
*
- * @param mixed $key
- * @param mixed $default
- * @return mixed
+ * @template TGetDefault
+ *
+ * @param TKey $key
+ * @param TGetDefault|(\Closure(): TGetDefault) $default
+ * @return TValue|TGetDefault
*/
public function get($key, $default = null)
{
- if ($this->offsetExists($key)) {
+ if (array_key_exists($key, $this->items)) {
return $this->items[$key];
}
return value($default);
}
+ /**
+ * Get an item from the collection by key or add it to collection if it does not exist.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return mixed
+ */
+ public function getOrPut($key, $value)
+ {
+ if (array_key_exists($key, $this->items)) {
+ return $this->items[$key];
+ }
+
+ $this->offsetSet($key, $value = value($value));
+
+ return $value;
+ }
+
/**
* Group an associative array by a field or using a callback.
*
- * @param array|callable|string $groupBy
+ * @param (callable(TValue, TKey): array-key)|array|string $groupBy
* @param bool $preserveKeys
- * @return static
+ * @return static>
*/
public function groupBy($groupBy, $preserveKeys = false)
{
- if (is_array($groupBy)) {
+ if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
$nextGroups = $groupBy;
$groupBy = array_shift($nextGroups);
@@ -446,7 +485,11 @@ public function groupBy($groupBy, $preserveKeys = false)
}
foreach ($groupKeys as $groupKey) {
- $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
+ $groupKey = match (true) {
+ is_bool($groupKey) => (int) $groupKey,
+ $groupKey instanceof \Stringable => (string) $groupKey,
+ default => $groupKey,
+ };
if (! array_key_exists($groupKey, $results)) {
$results[$groupKey] = new static;
@@ -468,8 +511,8 @@ public function groupBy($groupBy, $preserveKeys = false)
/**
* Key an associative array by a field or using a callback.
*
- * @param callable|string $keyBy
- * @return static
+ * @param (callable(TValue, TKey): array-key)|array|string $keyBy
+ * @return static
*/
public function keyBy($keyBy)
{
@@ -493,7 +536,7 @@ public function keyBy($keyBy)
/**
* Determine if an item exists in the collection by key.
*
- * @param mixed $key
+ * @param TKey|array $key
* @return bool
*/
public function has($key)
@@ -501,7 +544,7 @@ public function has($key)
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $value) {
- if (! $this->offsetExists($value)) {
+ if (! array_key_exists($value, $this->items)) {
return false;
}
}
@@ -509,28 +552,55 @@ public function has($key)
return true;
}
+ /**
+ * Determine if any of the keys exist in the collection.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function hasAny($key)
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $value) {
+ if ($this->has($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Concatenate values of a given key as a string.
*
- * @param string $value
- * @param string $glue
+ * @param callable|string $value
+ * @param string|null $glue
* @return string
*/
public function implode($value, $glue = null)
{
+ if ($this->useAsCallable($value)) {
+ return implode($glue ?? '', $this->map($value)->all());
+ }
+
$first = $this->first();
- if (is_array($first) || is_object($first)) {
- return implode($glue, $this->pluck($value)->all());
+ if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
+ return implode($glue ?? '', $this->pluck($value)->all());
}
- return implode($value, $this->items);
+ return implode($value ?? '', $this->items);
}
/**
* Intersect the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function intersect($items)
@@ -541,7 +611,7 @@ public function intersect($items)
/**
* Intersect the collection with the given items by key.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function intersectByKeys($items)
@@ -561,6 +631,16 @@ public function isEmpty()
return empty($this->items);
}
+ /**
+ * Determine if the collection contains a single item.
+ *
+ * @return bool
+ */
+ public function containsOneItem()
+ {
+ return $this->count() === 1;
+ }
+
/**
* Join all items from the collection using a string. The final items can use a separate glue string.
*
@@ -594,7 +674,7 @@ public function join($glue, $finalGlue = '')
/**
* Get the keys of the collection items.
*
- * @return static
+ * @return static
*/
public function keys()
{
@@ -604,9 +684,11 @@ public function keys()
/**
* Get the last item from the collection.
*
- * @param callable|null $callback
- * @param mixed $default
- * @return mixed
+ * @template TLastDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TLastDefault|(\Closure(): TLastDefault) $default
+ * @return TValue|TLastDefault
*/
public function last(callable $callback = null, $default = null)
{
@@ -616,9 +698,9 @@ public function last(callable $callback = null, $default = null)
/**
* Get the values of a given key.
*
- * @param string|array $value
+ * @param string|int|array $value
* @param string|null $key
- * @return static
+ * @return static
*/
public function pluck($value, $key = null)
{
@@ -628,16 +710,14 @@ public function pluck($value, $key = null)
/**
* Run a map over each of the items.
*
- * @param callable $callback
- * @return static
+ * @template TMapValue
+ *
+ * @param callable(TValue, TKey): TMapValue $callback
+ * @return static
*/
public function map(callable $callback)
{
- $keys = array_keys($this->items);
-
- $items = array_map($callback, $this->items, $keys);
-
- return new static(array_combine($keys, $items));
+ return new static(Arr::map($this->items, $callback));
}
/**
@@ -645,8 +725,11 @@ public function map(callable $callback)
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapToDictionaryKey of array-key
+ * @template TMapToDictionaryValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
*/
public function mapToDictionary(callable $callback)
{
@@ -674,8 +757,11 @@ public function mapToDictionary(callable $callback)
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapWithKeysKey of array-key
+ * @template TMapWithKeysValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static
*/
public function mapWithKeys(callable $callback)
{
@@ -695,7 +781,7 @@ public function mapWithKeys(callable $callback)
/**
* Merge the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function merge($items)
@@ -706,8 +792,10 @@ public function merge($items)
/**
* Recursively merge the collection with the given items.
*
- * @param mixed $items
- * @return static
+ * @template TMergeRecursiveValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
*/
public function mergeRecursive($items)
{
@@ -717,8 +805,10 @@ public function mergeRecursive($items)
/**
* Create a collection by using this collection for keys and another for its values.
*
- * @param mixed $values
- * @return static
+ * @template TCombineValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
+ * @return static
*/
public function combine($values)
{
@@ -728,7 +818,7 @@ public function combine($values)
/**
* Union the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function union($items)
@@ -749,8 +839,8 @@ public function nth($step, $offset = 0)
$position = 0;
- foreach ($this->items as $item) {
- if ($position % $step === $offset) {
+ foreach ($this->slice($offset)->items as $item) {
+ if ($position % $step === 0) {
$new[] = $item;
}
@@ -763,7 +853,7 @@ public function nth($step, $offset = 0)
/**
* Get the items with the specified keys.
*
- * @param mixed $keys
+ * @param \Illuminate\Support\Enumerable|array|string $keys
* @return static
*/
public function only($keys)
@@ -782,38 +872,57 @@ public function only($keys)
}
/**
- * Get and remove the last item from the collection.
+ * Get and remove the last N items from the collection.
*
- * @return mixed
+ * @param int $count
+ * @return static|TValue|null
*/
- public function pop()
+ public function pop($count = 1)
{
- return array_pop($this->items);
+ if ($count === 1) {
+ return array_pop($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_pop($this->items));
+ }
+
+ return new static($results);
}
/**
* Push an item onto the beginning of the collection.
*
- * @param mixed $value
- * @param mixed $key
+ * @param TValue $value
+ * @param TKey $key
* @return $this
*/
public function prepend($value, $key = null)
{
- $this->items = Arr::prepend($this->items, $value, $key);
+ $this->items = Arr::prepend($this->items, ...func_get_args());
return $this;
}
/**
- * Push an item onto the end of the collection.
+ * Push one or more items onto the end of the collection.
*
- * @param mixed $value
+ * @param TValue ...$values
* @return $this
*/
- public function push($value)
+ public function push(...$values)
{
- $this->items[] = $value;
+ foreach ($values as $value) {
+ $this->items[] = $value;
+ }
return $this;
}
@@ -821,7 +930,7 @@ public function push($value)
/**
* Push all of the given items onto the collection.
*
- * @param iterable $source
+ * @param iterable $source
* @return static
*/
public function concat($source)
@@ -838,9 +947,11 @@ public function concat($source)
/**
* Get and remove an item from the collection.
*
- * @param mixed $key
- * @param mixed $default
- * @return mixed
+ * @template TPullDefault
+ *
+ * @param TKey $key
+ * @param TPullDefault|(\Closure(): TPullDefault) $default
+ * @return TValue|TPullDefault
*/
public function pull($key, $default = null)
{
@@ -850,8 +961,8 @@ public function pull($key, $default = null)
/**
* Put an item in the collection by key.
*
- * @param mixed $key
- * @param mixed $value
+ * @param TKey $key
+ * @param TValue $value
* @return $this
*/
public function put($key, $value)
@@ -864,8 +975,8 @@ public function put($key, $value)
/**
* Get one or a specified number of items randomly from the collection.
*
- * @param int|null $number
- * @return static|mixed
+ * @param (callable(self): int)|int|null $number
+ * @return static|TValue
*
* @throws \InvalidArgumentException
*/
@@ -875,46 +986,17 @@ public function random($number = null)
return Arr::random($this->items);
}
- return new static(Arr::random($this->items, $number));
- }
-
- /**
- * Reduce the collection to a single value.
- *
- * @param callable $callback
- * @param mixed $initial
- * @return mixed
- */
- /*public function reduce(callable $callback, $initial = null)
- {
- return array_reduce($this->items, $callback, $initial);
- }*/
-
- /**
- * Reduce the collection to a single value.
- *
- * @template TReduceInitial
- * @template TReduceReturnType
- *
- * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback
- * @param TReduceInitial $initial
- * @return TReduceReturnType
- */
- public function reduce(callable $callback, $initial = null)
- {
- $result = $initial;
-
- foreach ($this as $key => $value) {
- $result = $callback($result, $value, $key);
+ if (is_callable($number)) {
+ return new static(Arr::random($this->items, $number($this)));
}
- return $result;
+ return new static(Arr::random($this->items, $number));
}
/**
* Replace the collection items with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function replace($items)
@@ -925,7 +1007,7 @@ public function replace($items)
/**
* Recursively replace the collection items with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function replaceRecursive($items)
@@ -946,9 +1028,9 @@ public function reverse()
/**
* Search the collection for a given value and return the corresponding key if successful.
*
- * @param mixed $value
+ * @param TValue|(callable(TValue,TKey): bool) $value
* @param bool $strict
- * @return mixed
+ * @return TKey|bool
*/
public function search($value, $strict = false)
{
@@ -966,19 +1048,36 @@ public function search($value, $strict = false)
}
/**
- * Get and remove the first item from the collection.
+ * Get and remove the first N items from the collection.
*
- * @return mixed
+ * @param int $count
+ * @return static|TValue|null
*/
- public function shift()
+ public function shift($count = 1)
{
- return array_shift($this->items);
+ if ($count === 1) {
+ return array_shift($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_shift($this->items));
+ }
+
+ return new static($results);
}
/**
* Shuffle the items in the collection.
*
- * @param int $seed
+ * @param int|null $seed
* @return static
*/
public function shuffle($seed = null)
@@ -986,6 +1085,22 @@ public function shuffle($seed = null)
return new static(Arr::shuffle($this->items, $seed));
}
+ /**
+ * Create chunks representing a "sliding window" view of the items in the collection.
+ *
+ * @param int $size
+ * @param int $step
+ * @return static
+ */
+ public function sliding($size = 2, $step = 1)
+ {
+ $chunks = floor(($this->count() - $size) / $step) + 1;
+
+ return static::times($chunks, function ($number) use ($size, $step) {
+ return $this->slice(($number - 1) * $step, $size);
+ });
+ }
+
/**
* Skip the first {$count} items.
*
@@ -997,11 +1112,33 @@ public function skip($count)
return $this->slice($count);
}
+ /**
+ * Skip items in the collection until the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipUntil($value)
+ {
+ return new static($this->lazy()->skipUntil($value)->all());
+ }
+
+ /**
+ * Skip items in the collection while the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipWhile($value)
+ {
+ return new static($this->lazy()->skipWhile($value)->all());
+ }
+
/**
* Slice the underlying collection array.
*
* @param int $offset
- * @param int $length
+ * @param int|null $length
* @return static
*/
public function slice($offset, $length = null)
@@ -1013,7 +1150,7 @@ public function slice($offset, $length = null)
* Split a collection into a certain number of groups.
*
* @param int $numberOfGroups
- * @return static
+ * @return static
*/
public function split($numberOfGroups)
{
@@ -1046,11 +1183,81 @@ public function split($numberOfGroups)
return $groups;
}
+ /**
+ * Split a collection into a certain number of groups, and fill the first groups completely.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function splitIn($numberOfGroups)
+ {
+ return $this->chunk(ceil($this->count() / $numberOfGroups));
+ }
+
+ /**
+ * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ * @throws \Illuminate\Support\MultipleItemsFoundException
+ */
+ public function sole($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $items = $this->unless($filter == null)->filter($filter);
+
+ $count = $items->count();
+
+ if ($count === 0) {
+ throw new ItemNotFoundException;
+ }
+
+ if ($count > 1) {
+ throw new MultipleItemsFoundException($count);
+ }
+
+ return $items->first();
+ }
+
+ /**
+ * Get the first item in the collection but throw an exception if no matching items exist.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ */
+ public function firstOrFail($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $placeholder = new stdClass();
+
+ $item = $this->first($filter, $placeholder);
+
+ if ($item === $placeholder) {
+ throw new ItemNotFoundException;
+ }
+
+ return $item;
+ }
+
/**
* Chunk the collection into chunks of the given size.
*
* @param int $size
- * @return static
+ * @return static
*/
public function chunk($size)
{
@@ -1067,19 +1274,47 @@ public function chunk($size)
return new static($chunks);
}
+ /**
+ * Chunk the collection into chunks with a callback.
+ *
+ * @param callable(TValue, TKey, static): bool $callback
+ * @return static>
+ */
+ public function chunkWhile(callable $callback)
+ {
+ return new static(
+ $this->lazy()->chunkWhile($callback)->mapInto(static::class)
+ );
+ }
+
/**
* Sort through each item with a callback.
*
- * @param callable|null $callback
+ * @param (callable(TValue, TValue): int)|null|int $callback
* @return static
*/
- public function sort(callable $callback = null)
+ public function sort($callback = null)
{
$items = $this->items;
- $callback
+ $callback && is_callable($callback)
? uasort($items, $callback)
- : asort($items);
+ : asort($items, $callback ?? SORT_REGULAR);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort items in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortDesc($options = SORT_REGULAR)
+ {
+ $items = $this->items;
+
+ arsort($items, $options);
return new static($items);
}
@@ -1087,20 +1322,24 @@ public function sort(callable $callback = null)
/**
* Sort the collection using the given callback.
*
- * @param callable|string $callback
+ * @param array|(callable(TValue, TKey): mixed)|string $callback
* @param int $options
* @param bool $descending
* @return static
*/
public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
{
+ if (is_array($callback) && ! is_callable($callback)) {
+ return $this->sortByMany($callback);
+ }
+
$results = [];
$callback = $this->valueRetriever($callback);
// First we will loop through the items and get the comparator from a callback
// function which we were given. Then, we will sort the returned values and
- // and grab the corresponding values for the sorted keys from this array.
+ // grab all the corresponding values for the sorted keys from this array.
foreach ($this->items as $key => $value) {
$results[$key] = $callback($value, $key);
}
@@ -1118,10 +1357,52 @@ public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
return new static($results);
}
+ /**
+ * Sort the collection using multiple comparisons.
+ *
+ * @param array $comparisons
+ * @return static
+ */
+ protected function sortByMany(array $comparisons = [])
+ {
+ $items = $this->items;
+
+ uasort($items, function ($a, $b) use ($comparisons) {
+ foreach ($comparisons as $comparison) {
+ $comparison = Arr::wrap($comparison);
+
+ $prop = $comparison[0];
+
+ $ascending = Arr::get($comparison, 1, true) === true ||
+ Arr::get($comparison, 1, true) === 'asc';
+
+ if (! is_string($prop) && is_callable($prop)) {
+ $result = $prop($a, $b);
+ } else {
+ $values = [data_get($a, $prop), data_get($b, $prop)];
+
+ if (! $ascending) {
+ $values = array_reverse($values);
+ }
+
+ $result = $values[0] <=> $values[1];
+ }
+
+ if ($result === 0) {
+ continue;
+ }
+
+ return $result;
+ }
+ });
+
+ return new static($items);
+ }
+
/**
* Sort the collection in descending order using the given callback.
*
- * @param callable|string $callback
+ * @param array|(callable(TValue, TKey): mixed)|string $callback
* @param int $options
* @return static
*/
@@ -1149,7 +1430,7 @@ public function sortKeys($options = SORT_REGULAR, $descending = false)
/**
* Sort the collection keys in descending order.
*
- * @param int $options
+ * @param int $options
* @return static
*/
public function sortKeysDesc($options = SORT_REGULAR)
@@ -1157,12 +1438,27 @@ public function sortKeysDesc($options = SORT_REGULAR)
return $this->sortKeys($options, true);
}
+ /**
+ * Sort the collection keys using a callback.
+ *
+ * @param callable(TKey, TKey): int $callback
+ * @return static
+ */
+ public function sortKeysUsing(callable $callback)
+ {
+ $items = $this->items;
+
+ uksort($items, $callback);
+
+ return new static($items);
+ }
+
/**
* Splice a portion of the underlying collection array.
*
* @param int $offset
* @param int|null $length
- * @param mixed $replacement
+ * @param array $replacement
* @return static
*/
public function splice($offset, $length = null, $replacement = [])
@@ -1171,7 +1467,7 @@ public function splice($offset, $length = null, $replacement = [])
return new static(array_splice($this->items, $offset));
}
- return new static(array_splice($this->items, $offset, $length, $replacement));
+ return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
}
/**
@@ -1189,10 +1485,32 @@ public function take($limit)
return $this->slice(0, $limit);
}
+ /**
+ * Take items in the collection until the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function takeUntil($value)
+ {
+ return new static($this->lazy()->takeUntil($value)->all());
+ }
+
+ /**
+ * Take items in the collection while the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function takeWhile($value)
+ {
+ return new static($this->lazy()->takeWhile($value)->all());
+ }
+
/**
* Transform each item in the collection using a callback.
*
- * @param callable $callback
+ * @param callable(TValue, TKey): TValue $callback
* @return $this
*/
public function transform(callable $callback)
@@ -1203,10 +1521,46 @@ public function transform(callable $callback)
}
/**
- * Reset the keys on the underlying array.
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @return static
+ */
+ public function undot()
+ {
+ return new static(Arr::undot($this->all()));
+ }
+
+ /**
+ * Return only unique items from the collection array.
*
+ * @param (callable(TValue, TKey): mixed)|string|null $key
+ * @param bool $strict
* @return static
*/
+ public function unique($key = null, $strict = false)
+ {
+ if (is_null($key) && $strict === false) {
+ return new static(array_unique($this->items, SORT_REGULAR));
+ }
+
+ $callback = $this->valueRetriever($key);
+
+ $exists = [];
+
+ return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+ if (in_array($id = $callback($item, $key), $exists, $strict)) {
+ return true;
+ }
+
+ $exists[] = $id;
+ });
+ }
+
+ /**
+ * Reset the keys on the underlying array.
+ *
+ * @return static
+ */
public function values()
{
return new static(array_values($this->items));
@@ -1218,8 +1572,10 @@ public function values()
* e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
* => [[1, 4], [2, 5], [3, 6]]
*
- * @param mixed ...$items
- * @return static
+ * @template TZipValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items
+ * @return static>
*/
public function zip($items)
{
@@ -1231,15 +1587,17 @@ public function zip($items)
return new static(func_get_args());
}, $this->items], $arrayableItems);
- return new static(call_user_func_array('array_map', $params));
+ return new static(array_map(...$params));
}
/**
* Pad collection to the specified length with a value.
*
+ * @template TPadValue
+ *
* @param int $size
- * @param mixed $value
- * @return static
+ * @param TPadValue $value
+ * @return static
*/
public function pad($size, $value)
{
@@ -1249,9 +1607,9 @@ public function pad($size, $value)
/**
* Get an iterator for the items.
*
- * @return \ArrayIterator
+ * @return \ArrayIterator
*/
- public function getIterator(): \ArrayIterator
+ public function getIterator(): Traversable
{
return new ArrayIterator($this->items);
}
@@ -1266,10 +1624,21 @@ public function count(): int
return count($this->items);
}
+ /**
+ * Count the number of items in the collection by a field or using a callback.
+ *
+ * @param (callable(TValue, TKey): mixed)|string|null $countBy
+ * @return static
+ */
+ public function countBy($countBy = null)
+ {
+ return new static($this->lazy()->countBy($countBy)->all());
+ }
+
/**
* Add an item to the collection.
*
- * @param mixed $item
+ * @param TValue $item
* @return $this
*/
public function add($item)
@@ -1282,7 +1651,7 @@ public function add($item)
/**
* Get a base Support collection instance from this collection.
*
- * @return \Illuminate\Support\Collection
+ * @return \Illuminate\Support\Collection
*/
public function toBase()
{
@@ -1292,19 +1661,19 @@ public function toBase()
/**
* Determine if an item exists at an offset.
*
- * @param mixed $key
+ * @param TKey $key
* @return bool
*/
public function offsetExists($key): bool
{
- return array_key_exists($key, $this->items);
+ return isset($this->items[$key]);
}
/**
* Get an item at a given offset.
*
- * @param mixed $key
- * @return mixed
+ * @param TKey $key
+ * @return TValue
*/
public function offsetGet($key): mixed
{
@@ -1314,8 +1683,8 @@ public function offsetGet($key): mixed
/**
* Set the item at a given offset.
*
- * @param mixed $key
- * @param mixed $value
+ * @param TKey|null $key
+ * @param TValue $value
* @return void
*/
public function offsetSet($key, $value): void
@@ -1330,7 +1699,7 @@ public function offsetSet($key, $value): void
/**
* Unset the item at a given offset.
*
- * @param string $key
+ * @param TKey $key
* @return void
*/
public function offsetUnset($key): void
diff --git a/src/Support/Enumerable.php b/src/Support/Enumerable.php
index 02d6601..7832393 100644
--- a/src/Support/Enumerable.php
+++ b/src/Support/Enumerable.php
@@ -1,20 +1,33 @@
+ * @extends \IteratorAggregate
+ */
interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
{
/**
* Create a new collection instance if the value isn't one already.
*
- * @param mixed $items
- * @return static
+ * @template TMakeKey of array-key
+ * @template TMakeValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items
+ * @return static
*/
public static function make($items = []);
@@ -22,27 +35,49 @@ public static function make($items = []);
* Create a new instance by invoking the callback a given amount of times.
*
* @param int $number
- * @param callable $callback
+ * @param callable|null $callback
* @return static
*/
public static function times($number, callable $callback = null);
/**
- * Wrap the given value in a collection if applicable.
+ * Create a collection with the given range.
*
- * @param mixed $value
+ * @param int $from
+ * @param int $to
* @return static
*/
+ public static function range($from, $to);
+
+ /**
+ * Wrap the given value in a collection if applicable.
+ *
+ * @template TWrapKey of array-key
+ * @template TWrapValue
+ *
+ * @param iterable $value
+ * @return static
+ */
public static function wrap($value);
/**
* Get the underlying items from the given collection if applicable.
*
- * @param array|static $value
- * @return array
+ * @template TUnwrapKey of array-key
+ * @template TUnwrapValue
+ *
+ * @param array|static $value
+ * @return array
*/
public static function unwrap($value);
+ /**
+ * Create a new instance with no items.
+ *
+ * @return static
+ */
+ public static function empty();
+
/**
* Get all items in the enumerable.
*
@@ -53,38 +88,38 @@ public function all();
/**
* Alias for the "avg" method.
*
- * @param callable|string|null $callback
- * @return mixed
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return float|int|null
*/
public function average($callback = null);
/**
* Get the median of a given key.
*
- * @param string|array|null $key
- * @return mixed
+ * @param string|array|null $key
+ * @return float|int|null
*/
public function median($key = null);
/**
* Get the mode of a given key.
*
- * @param string|array|null $key
- * @return array|null
+ * @param string|array|null $key
+ * @return array|null
*/
public function mode($key = null);
/**
* Collapse the items into a single enumerable.
*
- * @return static
+ * @return static
*/
public function collapse();
/**
* Alias for the "contains" method.
*
- * @param mixed $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -94,8 +129,8 @@ public function some($key, $operator = null, $value = null);
/**
* Determine if an item exists, using strict comparison.
*
- * @param mixed $key
- * @param mixed $value
+ * @param (callable(TValue): bool)|TValue|array-key $key
+ * @param TValue|null $value
* @return bool
*/
public function containsStrict($key, $value = null);
@@ -103,26 +138,47 @@ public function containsStrict($key, $value = null);
/**
* Get the average value of a given key.
*
- * @param callable|string|null $callback
- * @return mixed
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return float|int|null
*/
public function avg($callback = null);
/**
* Determine if an item exists in the enumerable.
*
- * @param mixed $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
*/
public function contains($key, $operator = null, $value = null);
+ /**
+ * Determine if an item is not contained in the collection.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function doesntContain($key, $operator = null, $value = null);
+
+ /**
+ * Cross join with the given lists, returning all possible permutations.
+ *
+ * @template TCrossJoinKey
+ * @template TCrossJoinValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists
+ * @return static>
+ */
+ public function crossJoin(...$lists);
+
/**
* Dump the collection and end the script.
*
* @param mixed ...$args
- * @return void
+ * @return never
*/
public function dd(...$args);
@@ -136,7 +192,7 @@ public function dump();
/**
* Get the items that are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diff($items);
@@ -144,8 +200,8 @@ public function diff($items);
/**
* Get the items that are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TValue, TValue): int $callback
* @return static
*/
public function diffUsing($items, callable $callback);
@@ -153,16 +209,16 @@ public function diffUsing($items, callable $callback);
/**
* Get the items whose keys and values are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diffAssoc($items);
/**
- * Get the items whose keys and values are not present in the given items.
+ * Get the items whose keys and values are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
* @return static
*/
public function diffAssocUsing($items, callable $callback);
@@ -170,16 +226,16 @@ public function diffAssocUsing($items, callable $callback);
/**
* Get the items whose keys are not present in the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function diffKeys($items);
/**
- * Get the items whose keys are not present in the given items.
+ * Get the items whose keys are not present in the given items, using the callback.
*
- * @param mixed $items
- * @param callable $callback
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
* @return static
*/
public function diffKeysUsing($items, callable $callback);
@@ -187,7 +243,7 @@ public function diffKeysUsing($items, callable $callback);
/**
* Retrieve duplicate items.
*
- * @param callable|null $callback
+ * @param (callable(TValue): bool)|string|null $callback
* @param bool $strict
* @return static
*/
@@ -196,7 +252,7 @@ public function duplicates($callback = null, $strict = false);
/**
* Retrieve duplicate items using strict comparison.
*
- * @param callable|null $callback
+ * @param (callable(TValue): bool)|string|null $callback
* @return static
*/
public function duplicatesStrict($callback = null);
@@ -204,7 +260,7 @@ public function duplicatesStrict($callback = null);
/**
* Execute a callback over each item.
*
- * @param callable $callback
+ * @param callable(TValue, TKey): mixed $callback
* @return $this
*/
public function each(callable $callback);
@@ -218,9 +274,9 @@ public function each(callable $callback);
public function eachSpread(callable $callback);
/**
- * Determine if all items pass the given test.
+ * Determine if all items pass the given truth test.
*
- * @param string|callable $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -230,7 +286,7 @@ public function every($key, $operator = null, $value = null);
/**
* Get all items except for those with the specified keys.
*
- * @param mixed $keys
+ * @param \Illuminate\Support\Enumerable|array $keys
* @return static
*/
public function except($keys);
@@ -238,64 +294,76 @@ public function except($keys);
/**
* Run a filter over each of the items.
*
- * @param callable|null $callback
+ * @param (callable(TValue): bool)|null $callback
* @return static
*/
public function filter(callable $callback = null);
/**
- * Apply the callback if the value is truthy.
+ * Apply the callback if the given "value" is (or resolves to) truthy.
+ *
+ * @template TWhenReturnType as null
*
* @param bool $value
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @param (callable($this): TWhenReturnType)|null $callback
+ * @param (callable($this): TWhenReturnType)|null $default
+ * @return $this|TWhenReturnType
*/
- public function when($value, callable $callback, callable $default = null);
+ public function when($value, callable $callback = null, callable $default = null);
/**
* Apply the callback if the collection is empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TWhenEmptyReturnType
+ *
+ * @param (callable($this): TWhenEmptyReturnType) $callback
+ * @param (callable($this): TWhenEmptyReturnType)|null $default
+ * @return $this|TWhenEmptyReturnType
*/
public function whenEmpty(callable $callback, callable $default = null);
/**
* Apply the callback if the collection is not empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TWhenNotEmptyReturnType
+ *
+ * @param callable($this): TWhenNotEmptyReturnType $callback
+ * @param (callable($this): TWhenNotEmptyReturnType)|null $default
+ * @return $this|TWhenNotEmptyReturnType
*/
public function whenNotEmpty(callable $callback, callable $default = null);
/**
- * Apply the callback if the value is falsy.
+ * Apply the callback if the given "value" is (or resolves to) truthy.
+ *
+ * @template TUnlessReturnType
*
* @param bool $value
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @param (callable($this): TUnlessReturnType) $callback
+ * @param (callable($this): TUnlessReturnType)|null $default
+ * @return $this|TUnlessReturnType
*/
public function unless($value, callable $callback, callable $default = null);
/**
* Apply the callback unless the collection is empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TUnlessEmptyReturnType
+ *
+ * @param callable($this): TUnlessEmptyReturnType $callback
+ * @param (callable($this): TUnlessEmptyReturnType)|null $default
+ * @return $this|TUnlessEmptyReturnType
*/
public function unlessEmpty(callable $callback, callable $default = null);
/**
* Apply the callback unless the collection is not empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TUnlessNotEmptyReturnType
+ *
+ * @param callable($this): TUnlessNotEmptyReturnType $callback
+ * @param (callable($this): TUnlessNotEmptyReturnType)|null $default
+ * @return $this|TUnlessNotEmptyReturnType
*/
public function unlessNotEmpty(callable $callback, callable $default = null);
@@ -309,6 +377,22 @@ public function unlessNotEmpty(callable $callback, callable $default = null);
*/
public function where($key, $operator = null, $value = null);
+ /**
+ * Filter items where the value for the given key is null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNull($key = null);
+
+ /**
+ * Filter items where the value for the given key is not null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNotNull($key = null);
+
/**
* Filter items by the given key value pair using strict comparison.
*
@@ -322,7 +406,7 @@ public function whereStrict($key, $value);
* Filter items by the given key value pair.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @param bool $strict
* @return static
*/
@@ -332,7 +416,7 @@ public function whereIn($key, $values, $strict = false);
* Filter items by the given key value pair using strict comparison.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereInStrict($key, $values);
@@ -341,7 +425,7 @@ public function whereInStrict($key, $values);
* Filter items such that the value of the given key is between the given values.
*
* @param string $key
- * @param array $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereBetween($key, $values);
@@ -350,7 +434,7 @@ public function whereBetween($key, $values);
* Filter items such that the value of the given key is not between the given values.
*
* @param string $key
- * @param array $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereNotBetween($key, $values);
@@ -359,7 +443,7 @@ public function whereNotBetween($key, $values);
* Filter items by the given key value pair.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @param bool $strict
* @return static
*/
@@ -369,25 +453,29 @@ public function whereNotIn($key, $values, $strict = false);
* Filter items by the given key value pair using strict comparison.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereNotInStrict($key, $values);
/**
- * Filter the items, removing any items that don't match the given type.
+ * Filter the items, removing any items that don't match the given type(s).
*
- * @param string $type
- * @return static
+ * @template TWhereInstanceOf
+ *
+ * @param class-string|array> $type
+ * @return static
*/
public function whereInstanceOf($type);
/**
* Get the first item from the enumerable passing the given truth test.
*
- * @param callable|null $callback
- * @param mixed $default
- * @return mixed
+ * @template TFirstDefault
+ *
+ * @param (callable(TValue,TKey): bool)|null $callback
+ * @param TFirstDefault|(\Closure(): TFirstDefault) $default
+ * @return TValue|TFirstDefault
*/
public function first(callable $callback = null, $default = null);
@@ -397,56 +485,74 @@ public function first(callable $callback = null, $default = null);
* @param string $key
* @param mixed $operator
* @param mixed $value
- * @return mixed
+ * @return TValue|null
*/
public function firstWhere($key, $operator = null, $value = null);
/**
- * Flip the values with their keys.
+ * Get a flattened array of the items in the collection.
*
+ * @param int $depth
* @return static
*/
+ public function flatten($depth = INF);
+
+ /**
+ * Flip the values with their keys.
+ *
+ * @return static
+ */
public function flip();
/**
* Get an item from the collection by key.
*
- * @param mixed $key
- * @param mixed $default
- * @return mixed
+ * @template TGetDefault
+ *
+ * @param TKey $key
+ * @param TGetDefault|(\Closure(): TGetDefault) $default
+ * @return TValue|TGetDefault
*/
public function get($key, $default = null);
/**
* Group an associative array by a field or using a callback.
*
- * @param array|callable|string $groupBy
+ * @param (callable(TValue, TKey): array-key)|array|string $groupBy
* @param bool $preserveKeys
- * @return static
+ * @return static>
*/
public function groupBy($groupBy, $preserveKeys = false);
/**
* Key an associative array by a field or using a callback.
*
- * @param callable|string $keyBy
- * @return static
+ * @param (callable(TValue, TKey): array-key)|array|string $keyBy
+ * @return static
*/
public function keyBy($keyBy);
/**
* Determine if an item exists in the collection by key.
*
- * @param mixed $key
+ * @param TKey|array $key
* @return bool
*/
public function has($key);
+ /**
+ * Determine if any of the keys exist in the collection.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function hasAny($key);
+
/**
* Concatenate values of a given key as a string.
*
* @param string $value
- * @param string $glue
+ * @param string|null $glue
* @return string
*/
public function implode($value, $glue = null);
@@ -454,7 +560,7 @@ public function implode($value, $glue = null);
/**
* Intersect the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function intersect($items);
@@ -462,7 +568,7 @@ public function intersect($items);
/**
* Intersect the collection with the given items by key.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function intersectByKeys($items);
@@ -481,6 +587,13 @@ public function isEmpty();
*/
public function isNotEmpty();
+ /**
+ * Determine if the collection contains a single item.
+ *
+ * @return bool
+ */
+ public function containsOneItem();
+
/**
* Join all items from the collection using a string. The final items can use a separate glue string.
*
@@ -493,24 +606,28 @@ public function join($glue, $finalGlue = '');
/**
* Get the keys of the collection items.
*
- * @return static
+ * @return static
*/
public function keys();
/**
* Get the last item from the collection.
*
- * @param callable|null $callback
- * @param mixed $default
- * @return mixed
+ * @template TLastDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TLastDefault|(\Closure(): TLastDefault) $default
+ * @return TValue|TLastDefault
*/
public function last(callable $callback = null, $default = null);
/**
* Run a map over each of the items.
*
- * @param callable $callback
- * @return static
+ * @template TMapValue
+ *
+ * @param callable(TValue, TKey): TMapValue $callback
+ * @return static
*/
public function map(callable $callback);
@@ -527,8 +644,11 @@ public function mapSpread(callable $callback);
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapToDictionaryKey of array-key
+ * @template TMapToDictionaryValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
*/
public function mapToDictionary(callable $callback);
@@ -537,8 +657,11 @@ public function mapToDictionary(callable $callback);
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapToGroupsKey of array-key
+ * @template TMapToGroupsValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
*/
public function mapToGroups(callable $callback);
@@ -547,31 +670,36 @@ public function mapToGroups(callable $callback);
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapWithKeysKey of array-key
+ * @template TMapWithKeysValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static
*/
public function mapWithKeys(callable $callback);
/**
* Map a collection and flatten the result by a single level.
*
- * @param callable $callback
- * @return static
+ * @param callable(TValue, TKey): mixed $callback
+ * @return static
*/
public function flatMap(callable $callback);
/**
* Map the values into a new class.
*
- * @param string $class
- * @return static
+ * @template TMapIntoValue
+ *
+ * @param class-string $class
+ * @return static
*/
public function mapInto($class);
/**
* Merge the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function merge($items);
@@ -579,23 +707,27 @@ public function merge($items);
/**
* Recursively merge the collection with the given items.
*
- * @param mixed $items
- * @return static
+ * @template TMergeRecursiveValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
*/
public function mergeRecursive($items);
/**
* Create a collection by using this collection for keys and another for its values.
*
- * @param mixed $values
- * @return static
+ * @template TCombineValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
+ * @return static
*/
public function combine($values);
/**
* Union the collection with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function union($items);
@@ -603,7 +735,7 @@ public function union($items);
/**
* Get the min value of a given key.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue):mixed)|string|null $callback
* @return mixed
*/
public function min($callback = null);
@@ -611,7 +743,7 @@ public function min($callback = null);
/**
* Get the max value of a given key.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue):mixed)|string|null $callback
* @return mixed
*/
public function max($callback = null);
@@ -628,7 +760,7 @@ public function nth($step, $offset = 0);
/**
* Get the items with the specified keys.
*
- * @param mixed $keys
+ * @param \Illuminate\Support\Enumerable|array|string $keys
* @return static
*/
public function only($keys);
@@ -645,17 +777,17 @@ public function forPage($page, $perPage);
/**
* Partition the collection into two arrays using the given callback or key.
*
- * @param callable|string $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
- * @return static
+ * @return static, static>
*/
public function partition($key, $operator = null, $value = null);
/**
* Push all of the given items onto the collection.
*
- * @param iterable $source
+ * @param iterable $source
* @return static
*/
public function concat($source);
@@ -664,7 +796,7 @@ public function concat($source);
* Get one or a specified number of items randomly from the collection.
*
* @param int|null $number
- * @return static|mixed
+ * @return static|TValue
*
* @throws \InvalidArgumentException
*/
@@ -673,16 +805,30 @@ public function random($number = null);
/**
* Reduce the collection to a single value.
*
- * @param callable $callback
- * @param mixed $initial
- * @return mixed
+ * @template TReduceInitial
+ * @template TReduceReturnType
+ *
+ * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback
+ * @param TReduceInitial $initial
+ * @return TReduceReturnType
*/
public function reduce(callable $callback, $initial = null);
+ /**
+ * Reduce the collection to multiple aggregate values.
+ *
+ * @param callable $callback
+ * @param mixed ...$initial
+ * @return array
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function reduceSpread(callable $callback, ...$initial);
+
/**
* Replace the collection items with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function replace($items);
@@ -690,7 +836,7 @@ public function replace($items);
/**
* Recursively replace the collection items with the given items.
*
- * @param mixed $items
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
* @return static
*/
public function replaceRecursive($items);
@@ -705,20 +851,29 @@ public function reverse();
/**
* Search the collection for a given value and return the corresponding key if successful.
*
- * @param mixed $value
+ * @param TValue|callable(TValue,TKey): bool $value
* @param bool $strict
- * @return mixed
+ * @return TKey|bool
*/
public function search($value, $strict = false);
/**
* Shuffle the items in the collection.
*
- * @param int $seed
+ * @param int|null $seed
* @return static
*/
public function shuffle($seed = null);
+ /**
+ * Create chunks representing a "sliding window" view of the items in the collection.
+ *
+ * @param int $size
+ * @param int $step
+ * @return static
+ */
+ public function sliding($size = 2, $step = 1);
+
/**
* Skip the first {$count} items.
*
@@ -727,11 +882,27 @@ public function shuffle($seed = null);
*/
public function skip($count);
+ /**
+ * Skip items in the collection until the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipUntil($value);
+
+ /**
+ * Skip items in the collection while the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipWhile($value);
+
/**
* Get a slice of items from the enumerable.
*
* @param int $offset
- * @param int $length
+ * @param int|null $length
* @return static
*/
public function slice($offset, $length = null);
@@ -740,30 +911,79 @@ public function slice($offset, $length = null);
* Split a collection into a certain number of groups.
*
* @param int $numberOfGroups
- * @return static
+ * @return static
*/
public function split($numberOfGroups);
+ /**
+ * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ * @throws \Illuminate\Support\MultipleItemsFoundException
+ */
+ public function sole($key = null, $operator = null, $value = null);
+
+ /**
+ * Get the first item in the collection but throw an exception if no matching items exist.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ */
+ public function firstOrFail($key = null, $operator = null, $value = null);
+
/**
* Chunk the collection into chunks of the given size.
*
* @param int $size
- * @return static
+ * @return static
*/
public function chunk($size);
+ /**
+ * Chunk the collection into chunks with a callback.
+ *
+ * @param callable(TValue, TKey, static): bool $callback
+ * @return static>
+ */
+ public function chunkWhile(callable $callback);
+
+ /**
+ * Split a collection into a certain number of groups, and fill the first groups completely.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function splitIn($numberOfGroups);
+
/**
* Sort through each item with a callback.
*
- * @param callable|null $callback
+ * @param (callable(TValue, TValue): int)|null|int $callback
* @return static
*/
- public function sort(callable $callback = null);
+ public function sort($callback = null);
+
+ /**
+ * Sort items in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortDesc($options = SORT_REGULAR);
/**
* Sort the collection using the given callback.
*
- * @param callable|string $callback
+ * @param array|(callable(TValue, TKey): mixed)|string $callback
* @param int $options
* @param bool $descending
* @return static
@@ -773,7 +993,7 @@ public function sortBy($callback, $options = SORT_REGULAR, $descending = false);
/**
* Sort the collection in descending order using the given callback.
*
- * @param callable|string $callback
+ * @param array|(callable(TValue, TKey): mixed)|string $callback
* @param int $options
* @return static
*/
@@ -791,15 +1011,23 @@ public function sortKeys($options = SORT_REGULAR, $descending = false);
/**
* Sort the collection keys in descending order.
*
- * @param int $options
+ * @param int $options
* @return static
*/
public function sortKeysDesc($options = SORT_REGULAR);
+ /**
+ * Sort the collection keys using a callback.
+ *
+ * @param callable(TKey, TKey): int $callback
+ * @return static
+ */
+ public function sortKeysUsing(callable $callback);
+
/**
* Get the sum of the given values.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue): mixed)|string|null $callback
* @return mixed
*/
public function sum($callback = null);
@@ -812,10 +1040,26 @@ public function sum($callback = null);
*/
public function take($limit);
+ /**
+ * Take items in the collection until the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function takeUntil($value);
+
+ /**
+ * Take items in the collection while the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function takeWhile($value);
+
/**
* Pass the collection to the given callback and then return it.
*
- * @param callable $callback
+ * @param callable(TValue): mixed $callback
* @return $this
*/
public function tap(callable $callback);
@@ -823,32 +1067,57 @@ public function tap(callable $callback);
/**
* Pass the enumerable to the given callback and return the result.
*
- * @param callable $callback
- * @return mixed
+ * @template TPipeReturnType
+ *
+ * @param callable($this): TPipeReturnType $callback
+ * @return TPipeReturnType
*/
public function pipe(callable $callback);
+ /**
+ * Pass the collection into a new class.
+ *
+ * @param class-string $class
+ * @return mixed
+ */
+ public function pipeInto($class);
+
+ /**
+ * Pass the collection through a series of callable pipes and return the result.
+ *
+ * @param array $pipes
+ * @return mixed
+ */
+ public function pipeThrough($pipes);
+
/**
* Get the values of a given key.
*
- * @param string|array $value
+ * @param string|array $value
* @param string|null $key
- * @return static
+ * @return static
*/
public function pluck($value, $key = null);
/**
* Create a collection of all elements that do not pass a given truth test.
*
- * @param callable|mixed $callback
+ * @param (callable(TValue, TKey): bool)|bool $callback
* @return static
*/
public function reject($callback = true);
+ /**
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @return static
+ */
+ public function undot();
+
/**
* Return only unique items from the collection array.
*
- * @param string|callable|null $key
+ * @param (callable(TValue, TKey): mixed)|string|null $key
* @param bool $strict
* @return static
*/
@@ -857,7 +1126,7 @@ public function unique($key = null, $strict = false);
/**
* Return only unique items from the collection array using strict comparison.
*
- * @param string|callable|null $key
+ * @param (callable(TValue, TKey): mixed)|string|null $key
* @return static
*/
public function uniqueStrict($key = null);
@@ -865,34 +1134,93 @@ public function uniqueStrict($key = null);
/**
* Reset the keys on the underlying array.
*
- * @return static
+ * @return static
*/
public function values();
/**
* Pad collection to the specified length with a value.
*
+ * @template TPadValue
+ *
* @param int $size
- * @param mixed $value
- * @return static
+ * @param TPadValue $value
+ * @return static
*/
public function pad($size, $value);
/**
- * Count the number of items in the collection using a given truth test.
+ * Get the values iterator.
*
- * @param callable|null $callback
- * @return static
+ * @return \Traversable
+ */
+ public function getIterator(): Traversable;
+
+ /**
+ * Count the number of items in the collection.
+ *
+ * @return int
+ */
+ public function count(): int;
+
+ /**
+ * Count the number of items in the collection by a field or using a callback.
+ *
+ * @param (callable(TValue, TKey): mixed)|string|null $countBy
+ * @return static
*/
public function countBy($callback = null);
+ /**
+ * Zip the collection together with one or more arrays.
+ *
+ * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+ * => [[1, 4], [2, 5], [3, 6]]
+ *
+ * @template TZipValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items
+ * @return static>
+ */
+ public function zip($items);
+
/**
* Collect the values into a collection.
*
- * @return \Illuminate\Support\Collection
+ * @return \Illuminate\Support\Collection
*/
public function collect();
+ /**
+ * Get the collection of items as a plain array.
+ *
+ * @return array
+ */
+ public function toArray();
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return mixed
+ */
+ public function jsonSerialize(): mixed;
+
+ /**
+ * Get the collection of items as JSON.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0);
+
+ /**
+ * Get a CachingIterator instance.
+ *
+ * @param int $flags
+ * @return \CachingIterator
+ */
+ public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING);
+
/**
* Convert the collection to its string representation.
*
@@ -900,6 +1228,14 @@ public function collect();
*/
public function __toString();
+ /**
+ * Indicate that the model's string representation should be escaped when __toString is invoked.
+ *
+ * @param bool $escape
+ * @return $this
+ */
+ public function escapeWhenCastingToString($escape = true);
+
/**
* Add a method to the list of proxied methods.
*
diff --git a/src/Support/Facades/Blade.php b/src/Support/Facades/Blade.php
deleted file mode 100644
index 2fa6499..0000000
--- a/src/Support/Facades/Blade.php
+++ /dev/null
@@ -1,22 +0,0 @@
-js = $this->convertDataToJavaScriptExpression($data, $flags, $depth);
- }
-
- /**
- * Create a new JavaScript string from the given data.
- *
- * @param mixed $data
- * @param int $flags
- * @param int $depth
- * @return static
- *
- * @throws \JsonException
- */
- public static function from($data, $flags = 0, $depth = 512)
- {
- return new static($data, $flags, $depth);
- }
-
- /**
- * Convert the given data to a JavaScript expression.
- *
- * @param mixed $data
- * @param int $flags
- * @param int $depth
- * @return string
- *
- * @throws \JsonException
- */
- protected function convertDataToJavaScriptExpression($data, $flags = 0, $depth = 512)
- {
- if ($data instanceof self) {
- return $data->toHtml();
- }
-
- $json = $this->jsonEncode($data, $flags, $depth);
-
- if (is_string($data)) {
- return "'".substr($json, 1, -1)."'";
- }
-
- return $this->convertJsonToJavaScriptExpression($json, $flags);
- }
-
- /**
- * Encode the given data as JSON.
- *
- * @param mixed $data
- * @param int $flags
- * @param int $depth
- * @return string
- *
- * @throws \JsonException
- */
- protected function jsonEncode($data, $flags = 0, $depth = 512)
- {
- if ($data instanceof Jsonable) {
- return $data->toJson($flags | static::REQUIRED_FLAGS);
- }
-
- if ($data instanceof Arrayable && ! ($data instanceof JsonSerializable)) {
- $data = $data->toArray();
- }
-
- return json_encode($data, $flags | static::REQUIRED_FLAGS, $depth);
- }
-
- /**
- * Convert the given JSON to a JavaScript expression.
- *
- * @param string $json
- * @param int $flags
- * @return string
- *
- * @throws \JsonException
- */
- protected function convertJsonToJavaScriptExpression($json, $flags = 0)
- {
- if ($json === '[]' || $json === '{}') {
- return $json;
- }
-
- if (Str::startsWith($json, ['"', '{', '['])) {
- return "JSON.parse('".substr(json_encode($json, $flags | static::REQUIRED_FLAGS), 1, -1)."')";
- }
-
- return $json;
- }
-
- /**
- * Get the string representation of the data for use in HTML.
- *
- * @return string
- */
- public function toHtml()
- {
- return $this->js;
- }
-
- /**
- * Get the string representation of the data for use in HTML.
- *
- * @return string
- */
- public function __toString()
- {
- return $this->toHtml();
- }
-}
diff --git a/src/Support/Pluralizer.php b/src/Support/Pluralizer.php
deleted file mode 100644
index 01cba35..0000000
--- a/src/Support/Pluralizer.php
+++ /dev/null
@@ -1,121 +0,0 @@
- $val) {
- $value = str_replace($val, $key, $value);
- }
-
- return preg_replace('/[^\x20-\x7E]/u', '', $value);
- }
-
/**
* Get the portion of a string before a given value.
*
@@ -271,59 +238,6 @@ public static function isJson($value)
return true;
}
- /**
- * Determine if a given value is a valid URL.
- *
- * @param mixed $value
- * @return bool
- */
- public static function isUrl($value)
- {
- if (! is_string($value)) {
- return false;
- }
-
- /*
- * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (5.0.7).
- *
- * (c) Fabien Potencier http://symfony.com
- */
- $pattern = '~^
- (aaa|aaas|about|acap|acct|acd|acr|adiumxtra|adt|afp|afs|aim|amss|android|appdata|apt|ark|attachment|aw|barion|beshare|bitcoin|bitcoincash|blob|bolo|browserext|calculator|callto|cap|cast|casts|chrome|chrome-extension|cid|coap|coap\+tcp|coap\+ws|coaps|coaps\+tcp|coaps\+ws|com-eventbrite-attendee|content|conti|crid|cvs|dab|data|dav|diaspora|dict|did|dis|dlna-playcontainer|dlna-playsingle|dns|dntp|dpp|drm|drop|dtn|dvb|ed2k|elsi|example|facetime|fax|feed|feedready|file|filesystem|finger|first-run-pen-experience|fish|fm|ftp|fuchsia-pkg|geo|gg|git|gizmoproject|go|gopher|graph|gtalk|h323|ham|hcap|hcp|http|https|hxxp|hxxps|hydrazone|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris\.beep|iris\.lwz|iris\.xpc|iris\.xpcs|isostore|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|leaptofrogans|lorawan|lvlt|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|mongodb|moz|ms-access|ms-browser-extension|ms-calculator|ms-drive-to|ms-enrollment|ms-excel|ms-eyecontrolspeech|ms-gamebarservices|ms-gamingoverlay|ms-getoffice|ms-help|ms-infopath|ms-inputapp|ms-lockscreencomponent-config|ms-media-stream-id|ms-mixedrealitycapture|ms-mobileplans|ms-officeapp|ms-people|ms-project|ms-powerpoint|ms-publisher|ms-restoretabcompanion|ms-screenclip|ms-screensketch|ms-search|ms-search-repair|ms-secondary-screen-controller|ms-secondary-screen-setup|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-connectabledevices|ms-settings-displays-topology|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|ms-spd|ms-sttoverlay|ms-transit-to|ms-useractivityset|ms-virtualtouchpad|ms-visio|ms-walk-to|ms-whiteboard|ms-whiteboard-cmd|ms-word|msnim|msrp|msrps|mss|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|ocf|oid|onenote|onenote-cmd|opaquelocktoken|openpgp4fpr|pack|palm|paparazzi|payto|pkcs11|platform|pop|pres|prospero|proxy|pwid|psyc|pttp|qb|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|s3|secondlife|service|session|sftp|sgn|shttp|sieve|simpleledger|sip|sips|skype|smb|sms|smtp|snews|snmp|soap\.beep|soap\.beeps|soldat|spiffe|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|tg|things|thismessage|tip|tn3270|tool|ts3server|turn|turns|tv|udp|unreal|urn|ut2004|v-event|vemmi|ventrilo|videotex|vnc|view-source|wais|webcal|wpid|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc\.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s):// # protocol
- (((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%[0-9A-Fa-f]{2})+)@)? # basic auth
- (
- ([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
- | # or
- \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
- | # or
- \[
- (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
- \] # an IPv6 address
- )
- (:[0-9]+)? # a port (optional)
- (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})* )* # a path
- (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a query (optional)
- (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%[0-9A-Fa-f]{2})* )? # a fragment (optional)
- $~ixu';
-
- return preg_match($pattern, $value) > 0;
- }
-
- /**
- * Determine if a given value is a valid UUID.
- *
- * @param mixed $value
- * @return bool
- */
- public static function isUuid($value)
- {
- if (! is_string($value)) {
- return false;
- }
-
- return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0;
- }
-
/**
* Convert a string to kebab case.
*
@@ -410,34 +324,6 @@ public static function parseCallback($callback, $default = null)
return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
}
- /**
- * Get the plural form of an English word.
- *
- * @param string $value
- * @param int $count
- * @return string
- */
- public static function plural($value, $count = 2)
- {
- return Pluralizer::plural($value, $count);
- }
-
- /**
- * Pluralize the last word of an English, studly caps case string.
- *
- * @param string $value
- * @param int $count
- * @return string
- */
- public static function pluralStudly($value, $count = 2)
- {
- $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
-
- $lastWord = array_pop($parts);
-
- return implode('', $parts).self::plural($lastWord, $count);
- }
-
/**
* Generate a more truly "random" alpha-numeric string.
*
@@ -586,17 +472,6 @@ public static function title($value)
return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
}
- /**
- * Get the singular form of an English word.
- *
- * @param string $value
- * @return string
- */
- public static function singular($value)
- {
- return Pluralizer::singular($value);
- }
-
/**
* Generate a URL friendly "slug" from a given string.
*
@@ -798,297 +673,4 @@ public static function wordCount($string, $characters = null)
{
return str_word_count($string, 0, $characters);
}
-
- /**
- * Generate a UUID (version 4).
- *
- * @return \Ramsey\Uuid\UuidInterface
- */
- public static function uuid()
- {
- return static::$uuidFactory
- ? call_user_func(static::$uuidFactory)
- : Uuid::uuid4();
- }
-
- /**
- * Generate a time-ordered UUID (version 4).
- *
- * @return \Ramsey\Uuid\UuidInterface
- */
- public static function orderedUuid()
- {
- if (static::$uuidFactory) {
- return call_user_func(static::$uuidFactory);
- }
-
- $factory = new UuidFactory();
-
- $factory->setRandomGenerator(new CombGenerator(
- $factory->getRandomGenerator(),
- $factory->getNumberConverter()
- ));
-
- $factory->setCodec(new TimestampFirstCombCodec(
- $factory->getUuidBuilder()
- ));
-
- return $factory->uuid4();
- }
-
- /**
- * Set the callable that will be used to generate UUIDs.
- *
- * @param callable $factory
- * @return void
- */
- public static function createUuidsUsing(callable $factory = null)
- {
- static::$uuidFactory = $factory;
- }
-
- /**
- * Set the sequence that will be used to generate UUIDs.
- *
- * @param array $sequence
- * @param callable|null $whenMissing
- * @return void
- */
- public static function createUuidsUsingSequence(array $sequence, $whenMissing = null)
- {
- $next = 0;
-
- $whenMissing ??= function () use (&$next) {
- $factoryCache = static::$uuidFactory;
-
- static::$uuidFactory = null;
-
- $uuid = static::uuid();
-
- static::$uuidFactory = $factoryCache;
-
- $next++;
-
- return $uuid;
- };
-
- static::createUuidsUsing(function () use (&$next, $sequence, $whenMissing) {
- if (array_key_exists($next, $sequence)) {
- return $sequence[$next++];
- }
-
- return $whenMissing();
- });
- }
-
- /**
- * Always return the same UUID when generating new UUIDs.
- *
- * @param \Closure|null $callback
- * @return \Ramsey\Uuid\UuidInterface
- */
- public static function freezeUuids(Closure $callback = null)
- {
- $uuid = Str::uuid();
-
- Str::createUuidsUsing(fn () => $uuid);
-
- if ($callback !== null) {
- try {
- $callback($uuid);
- } finally {
- Str::createUuidsNormally();
- }
- }
-
- return $uuid;
- }
-
- /**
- * Indicate that UUIDs should be created normally and not using a custom factory.
- *
- * @return void
- */
- public static function createUuidsNormally()
- {
- static::$uuidFactory = null;
- }
-
- /**
- * Returns the replacements for the ascii method.
- *
- * Note: Adapted from Stringy\Stringy.
- *
- * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
- *
- * @return array
- */
- protected static function charsArray()
- {
- static $charsArray;
-
- if (isset($charsArray)) {
- return $charsArray;
- }
-
- return $charsArray = [
- '0' => ['°', '₀', '۰', '0'],
- '1' => ['¹', '₁', '۱', '1'],
- '2' => ['²', '₂', '۲', '2'],
- '3' => ['³', '₃', '۳', '3'],
- '4' => ['⁴', '₄', '۴', '٤', '4'],
- '5' => ['⁵', '₅', '۵', '٥', '5'],
- '6' => ['⁶', '₆', '۶', '٦', '6'],
- '7' => ['⁷', '₇', '۷', '7'],
- '8' => ['⁸', '₈', '۸', '8'],
- '9' => ['⁹', '₉', '۹', '9'],
- 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä', 'א'],
- 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b', 'ב'],
- 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
- 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd', 'ד'],
- 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
- 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f', 'פ', 'ף'],
- 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g', 'ג'],
- 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h', 'ה'],
- 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i', 'י'],
- 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
- 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k', 'ק'],
- 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l', 'ל'],
- 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm', 'מ', 'ם'],
- 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n', 'נ'],
- 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
- 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p', 'פ', 'ף'],
- 'q' => ['ყ', 'q'],
- 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r', 'ר'],
- 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's', 'ס'],
- 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't', 'ת'],
- 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
- 'v' => ['в', 'ვ', 'ϐ', 'v', 'ו'],
- 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
- 'x' => ['χ', 'ξ', 'x'],
- 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
- 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z', 'ז'],
- 'aa' => ['ع', 'आ', 'آ'],
- 'ae' => ['æ', 'ǽ'],
- 'ai' => ['ऐ'],
- 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
- 'dj' => ['ђ', 'đ'],
- 'dz' => ['џ', 'ძ', 'דז'],
- 'ei' => ['ऍ'],
- 'gh' => ['غ', 'ღ'],
- 'ii' => ['ई'],
- 'ij' => ['ij'],
- 'kh' => ['х', 'خ', 'ხ'],
- 'lj' => ['љ'],
- 'nj' => ['њ'],
- 'oe' => ['ö', 'œ', 'ؤ'],
- 'oi' => ['ऑ'],
- 'oii' => ['ऒ'],
- 'ps' => ['ψ'],
- 'sh' => ['ш', 'შ', 'ش', 'ש'],
- 'shch' => ['щ'],
- 'ss' => ['ß'],
- 'sx' => ['ŝ'],
- 'th' => ['þ', 'ϑ', 'θ', 'ث', 'ذ', 'ظ'],
- 'ts' => ['ц', 'ც', 'წ'],
- 'ue' => ['ü'],
- 'uu' => ['ऊ'],
- 'ya' => ['я'],
- 'yu' => ['ю'],
- 'zh' => ['ж', 'ჟ', 'ژ'],
- '(c)' => ['©'],
- 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
- 'B' => ['Б', 'Β', 'ब', 'B'],
- 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
- 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
- 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
- 'F' => ['Ф', 'Φ', 'F'],
- 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
- 'H' => ['Η', 'Ή', 'Ħ', 'H'],
- 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
- 'J' => ['J'],
- 'K' => ['К', 'Κ', 'K'],
- 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
- 'M' => ['М', 'Μ', 'M'],
- 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
- 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
- 'P' => ['П', 'Π', 'P'],
- 'Q' => ['Q'],
- 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
- 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
- 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
- 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
- 'V' => ['В', 'V'],
- 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
- 'X' => ['Χ', 'Ξ', 'X'],
- 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
- 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
- 'AE' => ['Æ', 'Ǽ'],
- 'Ch' => ['Ч'],
- 'Dj' => ['Ђ'],
- 'Dz' => ['Џ'],
- 'Gx' => ['Ĝ'],
- 'Hx' => ['Ĥ'],
- 'Ij' => ['IJ'],
- 'Jx' => ['Ĵ'],
- 'Kh' => ['Х'],
- 'Lj' => ['Љ'],
- 'Nj' => ['Њ'],
- 'Oe' => ['Œ'],
- 'Ps' => ['Ψ'],
- 'Sh' => ['Ш', 'ש'],
- 'Shch' => ['Щ'],
- 'Ss' => ['ẞ'],
- 'Th' => ['Þ', 'Θ', 'ת'],
- 'Ts' => ['Ц'],
- 'Ya' => ['Я', 'יא'],
- 'Yu' => ['Ю', 'יו'],
- 'Zh' => ['Ж'],
- ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
- ];
- }
-
- /**
- * Returns the language specific replacements for the ascii method.
- *
- * Note: Adapted from Stringy\Stringy.
- *
- * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
- *
- * @param string $language
- * @return array|null
- */
- protected static function languageSpecificCharsArray($language)
- {
- static $languageSpecific;
-
- if (! isset($languageSpecific)) {
- $languageSpecific = [
- 'bg' => [
- ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
- ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
- ],
- 'da' => [
- ['æ', 'ø', 'å', 'Æ', 'Ø', 'Å'],
- ['ae', 'oe', 'aa', 'Ae', 'Oe', 'Aa'],
- ],
- 'de' => [
- ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
- ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
- ],
- 'he' => [
- ['א', 'ב', 'ג', 'ד', 'ה', 'ו'],
- ['ז', 'ח', 'ט', 'י', 'כ', 'ל'],
- ['מ', 'נ', 'ס', 'ע', 'פ', 'צ'],
- ['ק', 'ר', 'ש', 'ת', 'ן', 'ץ', 'ך', 'ם', 'ף'],
- ],
- 'ro' => [
- ['ă', 'â', 'î', 'ș', 'ț', 'Ă', 'Â', 'Î', 'Ș', 'Ț'],
- ['a', 'a', 'i', 's', 't', 'A', 'A', 'I', 'S', 'T'],
- ],
- ];
- }
-
- return $languageSpecific[$language] ?? null;
- }
}
diff --git a/src/Support/Stringable.php b/src/Support/Stringable.php
index 292741c..dc455be 100644
--- a/src/Support/Stringable.php
+++ b/src/Support/Stringable.php
@@ -5,10 +5,11 @@
use ArrayAccess;
use Closure;
use Illuminate\Support\Traits\Conditionable;
-
use JsonSerializable;
use Stringable as BaseStringable;
+use function Illuminate\Support\collect;
+
class Stringable implements JsonSerializable, ArrayAccess, BaseStringable
{
use Conditionable;
@@ -235,27 +236,6 @@ public function pipe(callable $callback)
return new static($callback($this));
}
- /**
- * Get the plural form of an English word.
- *
- * @param int|array|\Countable $count
- * @return static
- */
- public function plural($count = 2)
- {
- return new static(Str::plural($this->value, $count));
- }
-
- /**
- * Pluralize the last word of an English, studly caps case string.
- *
- * @param int|array|\Countable $count
- * @return static
- */
- public function pluralStudly($count = 2)
- {
- return new static(Str::pluralStudly($this->value, $count));
- }
/**
* Repeat the string.
diff --git a/src/Support/Traits/Conditionable.php b/src/Support/Traits/Conditionable.php
index aa77df5..5dd3108 100644
--- a/src/Support/Traits/Conditionable.php
+++ b/src/Support/Traits/Conditionable.php
@@ -1,4 +1,5 @@
*/
protected static $proxies = [
- 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first',
- 'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition',
- 'reject', 'some', 'sortBy', 'sortByDesc', 'sum', 'unique',
+ 'average',
+ 'avg',
+ 'contains',
+ 'doesntContain',
+ 'each',
+ 'every',
+ 'filter',
+ 'first',
+ 'flatMap',
+ 'groupBy',
+ 'keyBy',
+ 'map',
+ 'max',
+ 'min',
+ 'partition',
+ 'reject',
+ 'skipUntil',
+ 'skipWhile',
+ 'some',
+ 'sortBy',
+ 'sortByDesc',
+ 'sum',
+ 'takeUntil',
+ 'takeWhile',
+ 'unique',
+ 'unless',
+ 'until',
+ 'when',
];
/**
* Create a new collection instance if the value isn't one already.
*
- * @param mixed $items
- * @return static
+ * @template TMakeKey of array-key
+ * @template TMakeValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items
+ * @return static
*/
public static function make($items = [])
{
@@ -62,8 +115,11 @@ public static function make($items = [])
/**
* Wrap the given value in a collection if applicable.
*
- * @param mixed $value
- * @return static
+ * @template TWrapKey of array-key
+ * @template TWrapValue
+ *
+ * @param iterable $value
+ * @return static
*/
public static function wrap($value)
{
@@ -75,19 +131,52 @@ public static function wrap($value)
/**
* Get the underlying items from the given collection if applicable.
*
- * @param array|static $value
- * @return array
+ * @template TUnwrapKey of array-key
+ * @template TUnwrapValue
+ *
+ * @param array|static $value
+ * @return array
*/
public static function unwrap($value)
{
return $value instanceof Enumerable ? $value->all() : $value;
}
+ /**
+ * Create a new instance with no items.
+ *
+ * @return static
+ */
+ public static function empty()
+ {
+ return new static([]);
+ }
+
+ /**
+ * Create a new collection by invoking the callback a given amount of times.
+ *
+ * @template TTimesValue
+ *
+ * @param int $number
+ * @param (callable(int): TTimesValue)|null $callback
+ * @return static
+ */
+ public static function times($number, callable $callback = null)
+ {
+ if ($number < 1) {
+ return new static;
+ }
+
+ return static::range(1, $number)
+ ->unless($callback == null)
+ ->map($callback);
+ }
+
/**
* Alias for the "avg" method.
*
- * @param callable|string|null $callback
- * @return mixed
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return float|int|null
*/
public function average($callback = null)
{
@@ -97,7 +186,7 @@ public function average($callback = null)
/**
* Alias for the "contains" method.
*
- * @param mixed $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -110,16 +199,14 @@ public function some($key, $operator = null, $value = null)
/**
* Determine if an item exists, using strict comparison.
*
- * @param mixed $key
- * @param mixed $value
+ * @param (callable(TValue): bool)|TValue|array-key $key
+ * @param TValue|null $value
* @return bool
*/
public function containsStrict($key, $value = null)
{
if (func_num_args() === 2) {
- return $this->contains(function ($item) use ($key, $value) {
- return data_get($item, $key) === $value;
- });
+ return $this->contains(fn ($item) => data_get($item, $key) === $value);
}
if ($this->useAsCallable($key)) {
@@ -139,13 +226,13 @@ public function containsStrict($key, $value = null)
* Dump the items and end the script.
*
* @param mixed ...$args
- * @return void
+ * @return never
*/
public function dd(...$args)
{
- call_user_func_array([$this, 'dump'], $args);
+ $this->dump(...$args);
- die(1);
+ exit(1);
}
/**
@@ -155,8 +242,8 @@ public function dd(...$args)
*/
public function dump()
{
- (new static(func_get_args()))
- ->push($this)
+ (new Collection(func_get_args()))
+ ->push($this->all())
->each(function ($item) {
VarDumper::dump($item);
});
@@ -167,7 +254,7 @@ public function dump()
/**
* Execute a callback over each item.
*
- * @param callable $callback
+ * @param callable(TValue, TKey): mixed $callback
* @return $this
*/
public function each(callable $callback)
@@ -184,7 +271,7 @@ public function each(callable $callback)
/**
* Execute a callback over each nested chunk of items.
*
- * @param callable $callback
+ * @param callable(...mixed): mixed $callback
* @return static
*/
public function eachSpread(callable $callback)
@@ -197,9 +284,9 @@ public function eachSpread(callable $callback)
}
/**
- * Determine if all items pass the given test.
+ * Determine if all items pass the given truth test.
*
- * @param string|callable $key
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -224,16 +311,32 @@ public function every($key, $operator = null, $value = null)
/**
* Get the first item by the given key value pair.
*
- * @param string $key
+ * @param callable|string $key
* @param mixed $operator
* @param mixed $value
- * @return mixed
+ * @return TValue|null
*/
public function firstWhere($key, $operator = null, $value = null)
{
return $this->first($this->operatorForWhere(...func_get_args()));
}
+ /**
+ * Get a single key's value from the first matching item in the collection.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function value($key, $default = null)
+ {
+ if ($value = $this->firstWhere($key)) {
+ return data_get($value, $key, $default);
+ }
+
+ return value($default);
+ }
+
/**
* Determine if the collection is not empty.
*
@@ -247,8 +350,10 @@ public function isNotEmpty()
/**
* Run a map over each nested chunk of items.
*
- * @param callable $callback
- * @return static
+ * @template TMapSpreadValue
+ *
+ * @param callable(mixed): TMapSpreadValue $callback
+ * @return static
*/
public function mapSpread(callable $callback)
{
@@ -264,8 +369,11 @@ public function mapSpread(callable $callback)
*
* The callback should return an associative array with a single key/value pair.
*
- * @param callable $callback
- * @return static
+ * @template TMapToGroupsKey of array-key
+ * @template TMapToGroupsValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
*/
public function mapToGroups(callable $callback)
{
@@ -277,8 +385,8 @@ public function mapToGroups(callable $callback)
/**
* Map a collection and flatten the result by a single level.
*
- * @param callable $callback
- * @return static
+ * @param callable(TValue, TKey): mixed $callback
+ * @return static
*/
public function flatMap(callable $callback)
{
@@ -288,48 +396,42 @@ public function flatMap(callable $callback)
/**
* Map the values into a new class.
*
- * @param string $class
- * @return static
+ * @template TMapIntoValue
+ *
+ * @param class-string $class
+ * @return static
*/
public function mapInto($class)
{
- return $this->map(function ($value, $key) use ($class) {
- return new $class($value, $key);
- });
+ return $this->map(fn ($value, $key) => new $class($value, $key));
}
/**
* Get the min value of a given key.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue):mixed)|string|null $callback
* @return mixed
*/
public function min($callback = null)
{
$callback = $this->valueRetriever($callback);
- return $this->map(function ($value) use ($callback) {
- return $callback($value);
- })->filter(function ($value) {
- return ! is_null($value);
- })->reduce(function ($result, $value) {
- return is_null($result) || $value < $result ? $value : $result;
- });
+ return $this->map(fn ($value) => $callback($value))
+ ->filter(fn ($value) => ! is_null($value))
+ ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result);
}
/**
* Get the max value of a given key.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue):mixed)|string|null $callback
* @return mixed
*/
public function max($callback = null)
{
$callback = $this->valueRetriever($callback);
- return $this->filter(function ($value) {
- return ! is_null($value);
- })->reduce(function ($result, $item) use ($callback) {
+ return $this->filter(fn ($value) => ! is_null($value))->reduce(function ($result, $item) use ($callback) {
$value = $callback($item);
return is_null($result) || $value > $result ? $value : $result;
@@ -353,10 +455,10 @@ public function forPage($page, $perPage)
/**
* Partition the collection into two arrays using the given callback or key.
*
- * @param callable|string $key
- * @param mixed $operator
- * @param mixed $value
- * @return static
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
+ * @param TValue|string|null $operator
+ * @param TValue|null $value
+ * @return static, static>
*/
public function partition($key, $operator = null, $value = null)
{
@@ -381,49 +483,26 @@ public function partition($key, $operator = null, $value = null)
/**
* Get the sum of the given values.
*
- * @param callable|string|null $callback
+ * @param (callable(TValue): mixed)|string|null $callback
* @return mixed
*/
public function sum($callback = null)
{
- if (is_null($callback)) {
- $callback = function ($value) {
- return $value;
- };
- } else {
- $callback = $this->valueRetriever($callback);
- }
+ $callback = is_null($callback)
+ ? $this->identity()
+ : $this->valueRetriever($callback);
- return $this->reduce(function ($result, $item) use ($callback) {
- return $result + $callback($item);
- }, 0);
- }
-
- /**
- * Apply the callback if the value is truthy.
- *
- * @param bool $value
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
- */
- public function when($value, callable $callback, callable $default = null)
- {
- if ($value) {
- return $callback($this, $value);
- } elseif ($default) {
- return $default($this, $value);
- }
-
- return $this;
+ return $this->reduce(fn ($result, $item) => $result + $callback($item), 0);
}
/**
* Apply the callback if the collection is empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TWhenEmptyReturnType
+ *
+ * @param (callable($this): TWhenEmptyReturnType) $callback
+ * @param (callable($this): TWhenEmptyReturnType)|null $default
+ * @return $this|TWhenEmptyReturnType
*/
public function whenEmpty(callable $callback, callable $default = null)
{
@@ -433,34 +512,25 @@ public function whenEmpty(callable $callback, callable $default = null)
/**
* Apply the callback if the collection is not empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TWhenNotEmptyReturnType
+ *
+ * @param callable($this): TWhenNotEmptyReturnType $callback
+ * @param (callable($this): TWhenNotEmptyReturnType)|null $default
+ * @return $this|TWhenNotEmptyReturnType
*/
public function whenNotEmpty(callable $callback, callable $default = null)
{
return $this->when($this->isNotEmpty(), $callback, $default);
}
- /**
- * Apply the callback if the value is falsy.
- *
- * @param bool $value
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
- */
- public function unless($value, callable $callback, callable $default = null)
- {
- return $this->when(! $value, $callback, $default);
- }
-
/**
* Apply the callback unless the collection is empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TUnlessEmptyReturnType
+ *
+ * @param callable($this): TUnlessEmptyReturnType $callback
+ * @param (callable($this): TUnlessEmptyReturnType)|null $default
+ * @return $this|TUnlessEmptyReturnType
*/
public function unlessEmpty(callable $callback, callable $default = null)
{
@@ -470,9 +540,11 @@ public function unlessEmpty(callable $callback, callable $default = null)
/**
* Apply the callback unless the collection is not empty.
*
- * @param callable $callback
- * @param callable $default
- * @return static|mixed
+ * @template TUnlessNotEmptyReturnType
+ *
+ * @param callable($this): TUnlessNotEmptyReturnType $callback
+ * @param (callable($this): TUnlessNotEmptyReturnType)|null $default
+ * @return $this|TUnlessNotEmptyReturnType
*/
public function unlessNotEmpty(callable $callback, callable $default = null)
{
@@ -482,7 +554,7 @@ public function unlessNotEmpty(callable $callback, callable $default = null)
/**
* Filter items by the given key value pair.
*
- * @param string $key
+ * @param callable|string $key
* @param mixed $operator
* @param mixed $value
* @return static
@@ -492,6 +564,28 @@ public function where($key, $operator = null, $value = null)
return $this->filter($this->operatorForWhere(...func_get_args()));
}
+ /**
+ * Filter items where the value for the given key is null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNull($key = null)
+ {
+ return $this->whereStrict($key, null);
+ }
+
+ /**
+ * Filter items where the value for the given key is not null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNotNull($key = null)
+ {
+ return $this->where($key, '!==', null);
+ }
+
/**
* Filter items by the given key value pair using strict comparison.
*
@@ -508,7 +602,7 @@ public function whereStrict($key, $value)
* Filter items by the given key value pair.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @param bool $strict
* @return static
*/
@@ -516,16 +610,14 @@ public function whereIn($key, $values, $strict = false)
{
$values = $this->getArrayableItems($values);
- return $this->filter(function ($item) use ($key, $values, $strict) {
- return in_array(data_get($item, $key), $values, $strict);
- });
+ return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict));
}
/**
* Filter items by the given key value pair using strict comparison.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereInStrict($key, $values)
@@ -537,7 +629,7 @@ public function whereInStrict($key, $values)
* Filter items such that the value of the given key is between the given values.
*
* @param string $key
- * @param array $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereBetween($key, $values)
@@ -549,21 +641,21 @@ public function whereBetween($key, $values)
* Filter items such that the value of the given key is not between the given values.
*
* @param string $key
- * @param array $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereNotBetween($key, $values)
{
- return $this->filter(function ($item) use ($key, $values) {
- return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values);
- });
+ return $this->filter(
+ fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values)
+ );
}
/**
* Filter items by the given key value pair.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @param bool $strict
* @return static
*/
@@ -571,16 +663,14 @@ public function whereNotIn($key, $values, $strict = false)
{
$values = $this->getArrayableItems($values);
- return $this->reject(function ($item) use ($key, $values, $strict) {
- return in_array(data_get($item, $key), $values, $strict);
- });
+ return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict));
}
/**
* Filter items by the given key value pair using strict comparison.
*
* @param string $key
- * @param mixed $values
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
* @return static
*/
public function whereNotInStrict($key, $values)
@@ -589,14 +679,26 @@ public function whereNotInStrict($key, $values)
}
/**
- * Filter the items, removing any items that don't match the given type.
+ * Filter the items, removing any items that don't match the given type(s).
*
- * @param string $type
- * @return static
+ * @template TWhereInstanceOf
+ *
+ * @param class-string|array> $type
+ * @return static
*/
public function whereInstanceOf($type)
{
return $this->filter(function ($value) use ($type) {
+ if (is_array($type)) {
+ foreach ($type as $classType) {
+ if ($value instanceof $classType) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
return $value instanceof $type;
});
}
@@ -604,8 +706,10 @@ public function whereInstanceOf($type)
/**
* Pass the collection to the given callback and return the result.
*
- * @param callable $callback
- * @return mixed
+ * @template TPipeReturnType
+ *
+ * @param callable($this): TPipeReturnType $callback
+ * @return TPipeReturnType
*/
public function pipe(callable $callback)
{
@@ -613,22 +717,82 @@ public function pipe(callable $callback)
}
/**
- * Pass the collection to the given callback and then return it.
+ * Pass the collection into a new class.
+ *
+ * @param class-string $class
+ * @return mixed
+ */
+ public function pipeInto($class)
+ {
+ return new $class($this);
+ }
+
+ /**
+ * Pass the collection through a series of callable pipes and return the result.
+ *
+ * @param array $callbacks
+ * @return mixed
+ */
+ public function pipeThrough($callbacks)
+ {
+ return Collection::make($callbacks)->reduce(
+ fn ($carry, $callback) => $callback($carry),
+ $this,
+ );
+ }
+
+ /**
+ * Reduce the collection to a single value.
+ *
+ * @template TReduceInitial
+ * @template TReduceReturnType
+ *
+ * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback
+ * @param TReduceInitial $initial
+ * @return TReduceReturnType
+ */
+ public function reduce(callable $callback, $initial = null)
+ {
+ $result = $initial;
+
+ foreach ($this as $key => $value) {
+ $result = $callback($result, $value, $key);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Reduce the collection to multiple aggregate values.
*
* @param callable $callback
- * @return $this
+ * @param mixed ...$initial
+ * @return array
+ *
+ * @throws \UnexpectedValueException
*/
- public function tap(callable $callback)
+ public function reduceSpread(callable $callback, ...$initial)
{
- $callback(clone $this);
+ $result = $initial;
- return $this;
+ foreach ($this as $key => $value) {
+ $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
+
+ if (! is_array($result)) {
+ throw new UnexpectedValueException(sprintf(
+ "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.",
+ class_basename(static::class), gettype($result)
+ ));
+ }
+ }
+
+ return $result;
}
/**
* Create a collection of all elements that do not pass a given truth test.
*
- * @param callable|mixed $callback
+ * @param (callable(TValue, TKey): bool)|bool $callback
* @return static
*/
public function reject($callback = true)
@@ -642,10 +806,23 @@ public function reject($callback = true)
});
}
+ /**
+ * Pass the collection to the given callback and then return it.
+ *
+ * @param callable($this): mixed $callback
+ * @return $this
+ */
+ public function tap(callable $callback)
+ {
+ $callback($this);
+
+ return $this;
+ }
+
/**
* Return only unique items from the collection array.
*
- * @param string|callable|null $key
+ * @param (callable(TValue, TKey): mixed)|string|null $key
* @param bool $strict
* @return static
*/
@@ -667,7 +844,7 @@ public function unique($key = null, $strict = false)
/**
* Return only unique items from the collection array using strict comparison.
*
- * @param string|callable|null $key
+ * @param (callable(TValue, TKey): mixed)|string|null $key
* @return static
*/
public function uniqueStrict($key = null)
@@ -678,7 +855,7 @@ public function uniqueStrict($key = null)
/**
* Collect the values into a collection.
*
- * @return \Illuminate\Support\Collection
+ * @return \Illuminate\Support\Collection
*/
public function collect()
{
@@ -688,21 +865,19 @@ public function collect()
/**
* Get the collection of items as a plain array.
*
- * @return array
+ * @return array
*/
public function toArray()
{
- return $this->map(function ($value) {
- return $value instanceof Arrayable ? $value->toArray() : $value;
- })->all();
+ return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all();
}
/**
* Convert the object into something JSON serializable.
*
- * @return array
+ * @return array
*/
- public function jsonSerialize(): mixed
+ public function jsonSerialize(): array
{
return array_map(function ($value) {
if ($value instanceof JsonSerializable) {
@@ -740,32 +915,28 @@ public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
}
/**
- * Count the number of items in the collection using a given truth test.
+ * Convert the collection to its string representation.
*
- * @param callable|null $callback
- * @return static
+ * @return string
*/
- public function countBy($callback = null)
+ public function __toString()
{
- if (is_null($callback)) {
- $callback = function ($value) {
- return $value;
- };
- }
-
- return new static($this->groupBy($callback)->map(function ($value) {
- return $value->count();
- }));
+ return $this->escapeWhenCastingToString
+ ? e($this->toJson())
+ : $this->toJson();
}
/**
- * Convert the collection to its string representation.
+ * Indicate that the model's string representation should be escaped when __toString is invoked.
*
- * @return string
+ * @param bool $escape
+ * @return $this
*/
- public function __toString()
+ public function escapeWhenCastingToString($escape = true)
{
- return $this->toJson();
+ $this->escapeWhenCastingToString = $escape;
+
+ return $this;
}
/**
@@ -800,7 +971,7 @@ public function __get($key)
* Results array of items from Collection or Arrayable.
*
* @param mixed $items
- * @return array
+ * @return array
*/
protected function getArrayableItems($items)
{
@@ -816,6 +987,8 @@ protected function getArrayableItems($items)
return (array) $items->jsonSerialize();
} elseif ($items instanceof Traversable) {
return iterator_to_array($items);
+ } elseif ($items instanceof UnitEnum) {
+ return [$items];
}
return (array) $items;
@@ -824,13 +997,17 @@ protected function getArrayableItems($items)
/**
* Get an operator checker callback.
*
- * @param string $key
- * @param string $operator
+ * @param callable|string $key
+ * @param string|null $operator
* @param mixed $value
* @return \Closure
*/
protected function operatorForWhere($key, $operator = null, $value = null)
{
+ if ($this->useAsCallable($key)) {
+ return $key;
+ }
+
if (func_num_args() === 1) {
$value = true;
@@ -866,6 +1043,7 @@ protected function operatorForWhere($key, $operator = null, $value = null)
case '>=': return $retrieved >= $value;
case '===': return $retrieved === $value;
case '!==': return $retrieved !== $value;
+ case '<=>': return $retrieved <=> $value;
}
};
}
@@ -893,8 +1071,38 @@ protected function valueRetriever($value)
return $value;
}
- return function ($item) use ($value) {
- return data_get($item, $value);
- };
+ return fn ($item) => data_get($item, $value);
+ }
+
+ /**
+ * Make a function to check an item's equality.
+ *
+ * @param mixed $value
+ * @return \Closure(mixed): bool
+ */
+ protected function equality($value)
+ {
+ return fn ($item) => $item === $value;
+ }
+
+ /**
+ * Make a function using another function, by negating its result.
+ *
+ * @param \Closure $callback
+ * @return \Closure
+ */
+ protected function negate(Closure $callback)
+ {
+ return fn (...$params) => ! $callback(...$params);
+ }
+
+ /**
+ * Make a function that returns what's passed to it.
+ *
+ * @return \Closure(TValue): TValue
+ */
+ protected function identity()
+ {
+ return fn ($value) => $value;
}
}
diff --git a/src/Support/Traits/Macroable.php b/src/Support/Traits/Macroable.php
index e712327..fb65dfc 100644
--- a/src/Support/Traits/Macroable.php
+++ b/src/Support/Traits/Macroable.php
@@ -1,4 +1,5 @@
bindTo(null, static::class);
}
- return call_user_func_array(static::$macros[$method], $parameters);
+ return $macro(...$parameters);
}
/**
@@ -107,9 +119,9 @@ public function __call($method, $parameters)
$macro = static::$macros[$method];
if ($macro instanceof Closure) {
- return call_user_func_array($macro->bindTo($this, static::class), $parameters);
+ $macro = $macro->bindTo($this, static::class);
}
- return call_user_func_array($macro, $parameters);
+ return $macro(...$parameters);
}
}
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
index 37e8bc8..6d6e539 100644
--- a/src/Support/helpers.php
+++ b/src/Support/helpers.php
@@ -1,4 +1,5 @@
toHtml();
}
+ if (!is_string($value)) {
+ $value = (string) $value;
+ }
+
return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode);
}
-// }
+}
-if (! function_exists('e')) {
+if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
@@ -174,18 +190,6 @@ function data_get($target, $key, $default = null)
}
}
-// 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('windows_os')) {
/**
@@ -227,3 +231,14 @@ function hash_fit($data, $binary = false, $options = [])
return hash('xxh128', $data, $binary, $options);
}
}
+
+if (!function_exists('uuid')) {
+ function uuid()
+ {
+ $chars = md5(uniqid((string) mt_rand(), true));
+ $uuid = substr($chars, 0, 8) . '-' . substr($chars, 8, 4) . '-' . substr($chars, 12, 4) . '-'
+ . substr($chars, 16, 4) . '-'
+ . substr($chars, 20, 12);
+ return $uuid;
+ }
+}
\ No newline at end of file
diff --git a/src/View/AnonymousComponent.php b/src/View/AnonymousComponent.php
index eba6436..77dc5ea 100644
--- a/src/View/AnonymousComponent.php
+++ b/src/View/AnonymousComponent.php
@@ -1,4 +1,5 @@
attributes = $this->attributes ?: $this->newAttributeBag();
-
return array_merge(
($this->data['attributes'] ?? null)?->getAttributes() ?: [],
$this->attributes->getAttributes(),
diff --git a/src/View/AppendableAttributeValue.php b/src/View/AppendableAttributeValue.php
index f275801..b5983e9 100644
--- a/src/View/AppendableAttributeValue.php
+++ b/src/View/AppendableAttributeValue.php
@@ -1,4 +1,5 @@
cachePath)) {
- $contents = $this->compileString($this->files->get($this->getPath()));
+ $contents = $this->compileString($this->getViewContent($this->getPath()));
if (! empty($this->getPath())) {
$contents = $this->appendFilePath($contents);
@@ -179,7 +179,7 @@ public function compile($path = null)
$compiledPath = $this->getCompiledPath($this->getPath())
);
- $this->files->put($compiledPath, $contents);
+ $this->putViewContent($compiledPath, $contents);
}
}
@@ -443,6 +443,10 @@ protected function restoreRawContent($result)
*/
protected function getRawPlaceholder($replace)
{
+ if (is_numeric($replace)) {
+ $replace = (string) $replace;
+ }
+
return str_replace('#', $replace, '@__raw_block_#__@');
}
diff --git a/src/View/Compilers/Compiler.php b/src/View/Compilers/Compiler.php
index 24a6c26..db31b88 100644
--- a/src/View/Compilers/Compiler.php
+++ b/src/View/Compilers/Compiler.php
@@ -1,10 +1,11 @@
files = $files;
-
- $config = app('config')->get('view');
-
- /*
- if (! $cachePath) {
- throw new InvalidArgumentException('Please provide a valid cache path.');
- }*/
-
- if (empty($config['compiled'])) {
- $config['compiled'] = app()->getRuntimePath(); // . 'view' . DS
- }
-
- $this->cachePath = $config['compiled'];
- $this->isCache = $config['cache'] ?? false;
- $this->compiledExtension = $config['compiled_extension'] ?? 'php';
-
- $theme = $config['theme'] ?? '';
-
- // 设置到view文件夹
- $this->cachePath = $this->cachePath .'view'. DS;
-
- // 如果有主题, 则增加主题文件夹
- if ($theme) {
- $this->cachePath = $this->cachePath . $theme . DS;
- }
+ $this->cachePath = $cachePath;
+ $this->isCache = $shouldCache;
+ $this->compiledExtension = $compiledExtension;
}
/**
@@ -84,7 +61,6 @@ public function __construct(Filesystem $files/*, $cachePath, $isCache = true, $c
*/
public function getCompiledPath($path)
{
- // preg_match("/;app;([a-zA-Z]+);view;([a-zA-Z]+);/", str_replace('\\', ';', $path), $appendPaths);
// return $this->cachePath . '/' . sha1($path) . '.' . basename($path) .'.'. $this->compiledExtension;
return $this->cachePath .'/'. hash_fit('v2'. basename($path)) .'.'. $this->compiledExtension;
}
@@ -102,7 +78,7 @@ public function isExpired($path)
// If the compiled file doesn't exist we will indicate that the view is expired
// so that it can be re-compiled. Else, we will verify the last modification
// of the views is less than the modification times of the compiled views.
- if (! $this->files->exists($compiled)) {
+ if (! file_exists($compiled)) {
return true;
}
@@ -110,8 +86,7 @@ public function isExpired($path)
return true;
}
- return $this->files->lastModified($path) >=
- $this->files->lastModified($compiled);
+ return filemtime($path) >= filemtime($compiled);
}
/**
@@ -122,8 +97,65 @@ public function isExpired($path)
*/
protected function ensureCompiledDirectoryExists($path)
{
- if (! $this->files->exists(dirname($path))) {
- $this->files->makeDirectory(dirname($path), 0777, true, true);
+ if (! file_exists(dirname($path))) {
+ @mkdir(dirname($path), 0777, true);
+ }
+ }
+
+ /**
+ * Write the contents of a file.
+ *
+ * @param string $path
+ * @param string $contents
+ * @param bool $lock
+ * @return int|bool
+ */
+ public function putViewContent($path, $contents, $lock = false)
+ {
+ return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
+ }
+
+ /**
+ * Get the contents of a file.
+ *
+ * @param string $path
+ * @return string
+ */
+ public function getViewContent($path, $lock = false)
+ {
+ if (is_file($path)) {
+ return $lock ? $this->sharedGet($path) : file_get_contents($path);
}
+
+ throw new ViewException("View file does not exist at path {$path}");
+ }
+
+ /**
+ * Get contents of a file with shared access.
+ *
+ * @param string $path
+ * @return string
+ */
+ public function sharedGet($path)
+ {
+ $contents = '';
+
+ $handle = fopen($path, 'rb');
+
+ if ($handle) {
+ try {
+ if (flock($handle, LOCK_SH)) {
+ clearstatcache(true, $path);
+
+ $contents = fread($handle, $this->size($path) ?: 1);
+
+ flock($handle, LOCK_UN);
+ }
+ } finally {
+ fclose($handle);
+ }
+ }
+
+ return $contents;
}
}
diff --git a/src/View/Compilers/CompilerInterface.php b/src/View/Compilers/CompilerInterface.php
index dfcb023..cb13abf 100644
--- a/src/View/Compilers/CompilerInterface.php
+++ b/src/View/Compilers/CompilerInterface.php
@@ -1,4 +1,5 @@
* @author Taylor Otwell
@@ -61,8 +60,7 @@ public function __construct(array $aliases = [], array $namespaces = [], ?BladeC
{
$this->aliases = $aliases;
$this->namespaces = $namespaces;
-
- $this->blade = $blade ?: new BladeCompiler(new Filesystem, sys_get_temp_dir());
+ $this->blade = $blade ?: new BladeCompiler(sys_get_temp_dir());
}
/**
@@ -264,8 +262,6 @@ public function componentClass(string $component)
{
$viewFactory = Container::getInstance()->make('blade.view');
- // dd($this->aliases, $component);
-
if (isset($this->aliases[$component])) {
if (class_exists($alias = $this->aliases[$component])) {
return $alias;
@@ -414,10 +410,7 @@ public function findClassByComponent(string $component)
*/
public function guessClassName(string $component)
{
- /*$namespace = Container::getInstance()
- ->make(Application::class)
- ->getNamespace();*/
- $namespace = 'index\\';
+ $namespace = Container::getInstance()->getNamespace() ?: 'app\\';
$class = $this->formatClassName($component);
@@ -452,7 +445,7 @@ public function guessViewName($name, $prefix = 'components.')
$prefix .= '.';
}
- $delimiter = \Illuminate\View\ViewFinderInterface::HINT_PATH_DELIMITER;
+ $delimiter = ViewName::HINT_PATH_DELIMITER;
if (str_contains($name, $delimiter)) {
return Str::replaceFirst($delimiter, $delimiter.$prefix, $name);
diff --git a/src/View/Compilers/Concerns/CompilesAuthorizations.php b/src/View/Compilers/Concerns/CompilesAuthorizations.php
index 35496df..f21aa81 100644
--- a/src/View/Compilers/Concerns/CompilesAuthorizations.php
+++ b/src/View/Compilers/Concerns/CompilesAuthorizations.php
@@ -1,4 +1,5 @@
compileEchoDefaults($matches[2])}; ?>{$whitespace}";
+ return $matches[1]
+ ? substr($matches[0], 1)
+ : "wrapInEchoHandler($matches[2])}; ?>{$whitespace}";
};
return preg_replace_callback($pattern, $callback, $value);
@@ -91,8 +100,7 @@ protected function compileRegularEchos($value)
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3];
- // $wrapped = sprintf($this->echoFormat, $matches[2]);
- $wrapped = sprintf($this->echoFormat, $this->compileEchoDefaults($matches[2]));
+ $wrapped = sprintf($this->echoFormat, $this->wrapInEchoHandler($matches[2]));
return $matches[1] ? substr($matches[0], 1) : "{$whitespace}";
};
@@ -115,22 +123,12 @@ protected function compileEscapedEchos($value)
return $matches[1]
? $matches[0]
- : "compileEchoDefaults($matches[2])}); ?>{$whitespace}";
+ : "wrapInEchoHandler($matches[2])}); ?>{$whitespace}";
};
return preg_replace_callback($pattern, $callback, $value);
}
- /**
- * Compile the default values for the echo statement.
- *
- * @param string $value
- * @return string
- */
- public function compileEchoDefaults($value) {
- return preg_replace('/^(?=\$)(.+?)(?:\s+and\s+)(.+?)$/s', 'empty($1) ? $2 : $1', $value);
- }
-
/**
* Add an instance of the blade echo handler to the start of the compiled string.
*
@@ -158,7 +156,7 @@ protected function wrapInEchoHandler($value)
return empty($this->echoHandlers) ? $value : '$__bladeCompiler->applyEchoHandler('.$value.')';
}
-
+
/**
* Apply the echo handler for the value if it exists.
*
@@ -173,4 +171,100 @@ public function applyEchoHandler($value)
return $value;
}
+
+ /**
+ * Compile the default values for the echo statement.
+ *
+ * @param string $value
+ * @return string
+ */
+ public function compileEchoDefaults($value) {
+ return preg_replace('/^(?=\$)(.+?)(?:\s+and\s+)(.+?)$/s', 'empty($1) ? $2 : $1', $value);
+ }
+
+ /**
+ * Get the class name of the first parameter of the given Closure.
+ *
+ * @param \Closure $closure
+ * @return string
+ *
+ * @throws \ReflectionException
+ * @throws \RuntimeException
+ */
+ protected function firstClosureParameterType(Closure $closure)
+ {
+ $types = array_values($this->closureParameterTypes($closure));
+
+ if (! $types) {
+ throw new RuntimeException('The given Closure has no parameters.');
+ }
+
+ if ($types[0] === null) {
+ throw new RuntimeException('The first parameter of the given Closure is missing a type hint.');
+ }
+
+ return $types[0];
+ }
+
+ /**
+ * Get the class names / types of the parameters of the given Closure.
+ *
+ * @param \Closure $closure
+ * @return array
+ *
+ * @throws \ReflectionException
+ */
+ protected function closureParameterTypes(Closure $closure)
+ {
+ $reflection = new ReflectionFunction($closure);
+
+ return collect($reflection->getParameters())->mapWithKeys(function ($parameter) {
+ if ($parameter->isVariadic()) {
+ return [$parameter->getName() => null];
+ }
+
+ return [$parameter->getName() => self::getParameterClassName($parameter)];
+ })->all();
+ }
+
+ /**
+ * Get the class name of the given parameter's type, if possible.
+ *
+ * @param \ReflectionParameter $parameter
+ * @return string|null
+ */
+ public static function getParameterClassName($parameter)
+ {
+ $type = $parameter->getType();
+
+ if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
+ return;
+ }
+
+ return static::getTypeName($parameter, $type);
+ }
+
+ /**
+ * Get the given type's class name.
+ *
+ * @param \ReflectionParameter $parameter
+ * @param \ReflectionNamedType $type
+ * @return string
+ */
+ protected static function getTypeName($parameter, $type)
+ {
+ $name = $type->getName();
+
+ if (! is_null($class = $parameter->getDeclaringClass())) {
+ if ($name === 'self') {
+ return $class->getName();
+ }
+
+ if ($name === 'parent' && $parent = $class->getParentClass()) {
+ return $parent->getName();
+ }
+ }
+
+ return $name;
+ }
}
diff --git a/src/View/Compilers/Concerns/CompilesErrors.php b/src/View/Compilers/Concerns/CompilesErrors.php
deleted file mode 100644
index 252871d..0000000
--- a/src/View/Compilers/Concerns/CompilesErrors.php
+++ /dev/null
@@ -1,34 +0,0 @@
-stripParentheses($expression);
-
- return 'has('.$expression.')) :
-if (isset($message)) { $messageCache = $message; }
-$message = $errors->first('.$expression.'); ?>';
- }
-
- /**
- * Compile the enderror statements into valid PHP.
- *
- * @param string $expression
- * @return string
- */
- protected function compileEnderror($expression)
- {
- return '';
- }
-}
diff --git a/src/View/Compilers/Concerns/CompilesFragments.php b/src/View/Compilers/Concerns/CompilesFragments.php
index 607b6dd..c3c96a8 100644
--- a/src/View/Compilers/Concerns/CompilesFragments.php
+++ b/src/View/Compilers/Concerns/CompilesFragments.php
@@ -1,4 +1,5 @@
toHtml() ?>",
- Js::class, $this->stripParentheses($expression)
- );
- }
-}
diff --git a/src/View/Compilers/Concerns/CompilesLayouts.php b/src/View/Compilers/Concerns/CompilesLayouts.php
index aaef617..34d2207 100644
--- a/src/View/Compilers/Concerns/CompilesLayouts.php
+++ b/src/View/Compilers/Concerns/CompilesLayouts.php
@@ -1,4 +1,5 @@
hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.');
$__env->startPush('.$stack.'); ?>';
@@ -89,7 +90,7 @@ protected function compilePrependOnce($expression)
[$stack, $id] = [$parts[0], $parts[1] ?? ''];
- $id = trim($id) ?: "'".(string) Str::uuid()."'";
+ $id = trim($id) ?: "'".(string) uuid()."'";
return 'hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.');
$__env->startPrepend('.$stack.'); ?>';
diff --git a/src/View/Compilers/Concerns/CompilesStyles.php b/src/View/Compilers/Concerns/CompilesStyles.php
index 6c71506..f57569f 100644
--- a/src/View/Compilers/Concerns/CompilesStyles.php
+++ b/src/View/Compilers/Concerns/CompilesStyles.php
@@ -1,4 +1,5 @@
componentData[count($this->componentStack)],
['slot' => $defaultSlot],
$this->slots[count($this->componentStack)],
- ['__laravel_slots' => $slots]
+ ['__thinkphp_slots' => $slots]
);
}
diff --git a/src/View/Concerns/ManagesFragments.php b/src/View/Concerns/ManagesFragments.php
index 7273da6..a243516 100644
--- a/src/View/Concerns/ManagesFragments.php
+++ b/src/View/Concerns/ManagesFragments.php
@@ -1,4 +1,5 @@
compileProps($bindings),
$this->compileBindings($bindings),
class_exists($class) ? '{{ $attributes }}' : '',
- $this->compileSlots($data['__laravel_slots']),
+ $this->compileSlots($data['__thinkphp_slots']),
'{{ $slot ?? "" }}',
],
$template
@@ -162,10 +163,11 @@ protected function bindings(string $class)
protected function compiler()
{
if (! static::$compiler) {
+ $blade = Container::getInstance()->make('blade.compiler');
static::$compiler = new ComponentTagCompiler(
- Container::getInstance()->make('blade.compiler')->getClassComponentAliases(),
- Container::getInstance()->make('blade.compiler')->getClassComponentNamespaces(),
- Container::getInstance()->make('blade.compiler')
+ $blade->getClassComponentAliases(),
+ $blade->getClassComponentNamespaces(),
+ $blade
);
}
diff --git a/src/View/Engines/CompilerEngine.php b/src/View/Engines/CompilerEngine.php
index 93f54c7..0f7e9e6 100644
--- a/src/View/Engines/CompilerEngine.php
+++ b/src/View/Engines/CompilerEngine.php
@@ -5,7 +5,6 @@
use ErrorException;
use Exception;
use Throwable;
-use Illuminate\Filesystem\Filesystem;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\View\Compilers\CompilerInterface;
use Illuminate\View\ViewException;
@@ -14,7 +13,6 @@
use function Illuminate\Support\last;
use function Illuminate\Support\str;
-
class CompilerEngine extends PhpEngine
{
/**
@@ -42,12 +40,11 @@ class CompilerEngine extends PhpEngine
* Create a new compiler engine instance.
*
* @param \Illuminate\View\Compilers\CompilerInterface $compiler
- * @param \Illuminate\Filesystem\Filesystem|null $files
* @return void
*/
- public function __construct(CompilerInterface $compiler, Filesystem $files = null)
+ public function __construct(CompilerInterface $compiler)
{
- parent::__construct($files ?: new Filesystem);
+ parent::__construct();
$this->compiler = $compiler;
}
diff --git a/src/View/Engines/PhpEngine.php b/src/View/Engines/PhpEngine.php
index 13525ae..d3061d6 100644
--- a/src/View/Engines/PhpEngine.php
+++ b/src/View/Engines/PhpEngine.php
@@ -3,28 +3,18 @@
namespace Illuminate\View\Engines;
use Illuminate\Contracts\View\Engine;
-use Illuminate\Filesystem\Filesystem;
+use Illuminate\View\ViewException;
use Throwable;
class PhpEngine implements Engine
{
- /**
- * The filesystem instance.
- *
- * @var \Illuminate\Filesystem\Filesystem
- */
- protected $files;
-
/**
* Create a new file engine instance.
*
- * @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
- public function __construct(Filesystem $files)
- {
- $this->files = $files;
- }
+ public function __construct()
+ {}
/**
* Get the evaluated contents of the view.
@@ -55,7 +45,7 @@ protected function evaluatePath($path, $data)
// flush out any stray output that might get out before an error occurs or
// an exception is thrown. This prevents any partial views from leaking.
try {
- $this->files->getRequire($path, $data);
+ $this->getRequire($path, $data);
} catch (Throwable $e) {
$this->handleViewException($e, $obLevel);
}
@@ -80,4 +70,29 @@ protected function handleViewException(Throwable $e, $obLevel)
throw $e;
}
+
+ /**
+ * Get the returned value of a file.
+ *
+ * @param string $path
+ * @param array $data
+ * @return mixed
+ *
+ * @throws \Illuminate\View\ViewException
+ */
+ public function getRequire($path, array $data = [])
+ {
+ if (is_file($path)) {
+ $__path = $path;
+ $__data = $data;
+
+ return (static function () use ($__path, $__data) {
+ extract($__data, EXTR_SKIP);
+
+ return require $__path;
+ })();
+ }
+
+ throw new ViewException("View file does not exist at path {$path}.");
+ }
}
diff --git a/src/View/Factory.php b/src/View/Factory.php
index c274bc1..c7942f4 100644
--- a/src/View/Factory.php
+++ b/src/View/Factory.php
@@ -1,4 +1,5 @@
'view',
// 模版主题
'theme' => '',
- // 模板起始路径
- 'base_path' => '',
- // 模板文件后缀
- 'suffix' => 'blade.php',
- // 模板文件名分隔符
- 'depr' => DS,
// 缓存路径
'compiled' => '',
+ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+ 'auto_rule' => 1,
+ // 视图目录名
+ 'view_dir_name' => 'view',
+ // 模板起始路径
+ 'view_path' => '',
+ // 模板后缀
+ 'view_suffix' => 'html.php',
+ // 模板文件名分隔符
+ 'view_depr' => DS,
// 是否开启模板编译缓存,设为false则每次都会重新编译
- 'cache' => true,
+ 'tpl_cache' => true,
];
/**
@@ -78,7 +82,6 @@ class Factory implements FactoryContract
* @var array
*/
protected $extensions = [
- 'html.php' => 'blade',
'blade.php' => 'blade',
'php' => 'php',
'css' => 'file',
@@ -111,78 +114,21 @@ class Factory implements FactoryContract
/**
* Create a new view factory instance.
*
+ * @param \think\App $app
* @param \Illuminate\View\Engines\EngineResolver $engines
* @param \Illuminate\View\ViewFinderInterface $finder
* @return void
*/
- public function __construct(EngineResolver $engines, $finder, App $app)
+ public function __construct(App $app, EngineResolver $engines/*, FileViewFinder $finder*/)
{
$this->app = $app;
- $this->finder = $finder;
+ // $this->finder = $finder;
$this->engines = $engines;
-
- $config = $this->app->get('config')->get('view');
-
- $this->config = array_merge($this->config, $config);
-
- if (empty($this->config['compiled'])) {
- $this->config['compiled'] = $app->getRuntimePath();
- }
-
- // 缓存主题路径
- if (!empty($this->config['theme'])) {
- $this->config['compiled'] .= $this->config['theme'] . DS;
- }
-
- // default view path
- $cutrrentApp = $this->app->http->getName();
-
- if ($this->config['base_path']) {
- $path = $this->config['base_path'];
- } else {
- $appName = $cutrrentApp;
- $view = $this->config['dir_name'];
-
- if (is_dir($this->app->getAppPath() . $view)) {
- $path = $this->app->getAppPath() . $view . DS;
- } else {
- $path = $this->app->getRootPath() . $view . DS . ($appName ? $appName . DS : '');
- }
- }
-
- // 设置主题路径
- if (!empty($this->config['theme'])) {
- // default 主题备用
- $path .= $this->config['theme'] . DS;
- }
-
- $this->app->get('view.finder')->addLocation($path);
-
- // $finder->addLocation($path); // . 'components'. DS
-
- // debug 不缓存
- if ($this->app->isDebug()) {
- // $this->config['cache'] = false;
- }
+ $this->config = array_merge($this->config, $this->app->get('config')->get('view', []));
$this->share('__env', $this);
}
- /**
- * 设置模板主题
- *
- * @param string $path 模板文件路径
- * @return bool
- */
- public function theme($path = '')
- {
- if (empty($this->config['theme'])) {
- return $path;
- }
-
- return $path .= $this->config['theme'] . DS;
- }
-
/**
* 根据模版获取实际路径
*
@@ -193,7 +139,7 @@ public function findView($template = '')
{
$templatePath = '';
- $template = $this->viewName($template);
+ $template = ViewName::normalize2tp($template);
if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
$templatePath = $this->parseTemplate($template);
@@ -201,7 +147,7 @@ public function findView($template = '')
// 模板不存在 抛出异常
if (!$templatePath || !is_file($templatePath)) {
- throw new ViewNotFoundException('View not exists:' . $this->viewName($template, true), $templatePath);
+ throw new ViewNotFoundException('View not exists:' . ViewName::normalize2tp($template, true), $templatePath);
}
return $templatePath;
@@ -216,7 +162,7 @@ public function findView($template = '')
*/
public function exists(string $view): bool
{
- $view = $this->viewName($view);
+ $view = ViewName::normalize2tp($view);
if ('' == pathinfo($view, PATHINFO_EXTENSION)) {
$view = $this->parseTemplate($view);
@@ -235,28 +181,29 @@ public function exists(string $view): bool
*/
public function make($view, $data = [], $mergeData = [])
{
+ $path = null;
if (is_file($view)) {
$path = $view;
} else {
- $path = '';
- // $view = ViewName::normalize2($view);
+ $_view = ViewName::normalize2tp($view);
- $view = $this->viewName($view);
- if ('' == pathinfo($view, PATHINFO_EXTENSION)) {
- $path = $this->parseTemplate($view);
+ // thinkphp方式查找模版
+ if ('' == pathinfo($_view, PATHINFO_EXTENSION)) {
+ $path = $this->parseTemplate($_view);
}
- if (!$path || !is_file($path)) {
+ // blade方式查找模版
+ /*if (!is_file($path)) {
$path = $this->finder->find(
$this->normalizeName($view)
);
- }
+ }*/
}
// 模板不存在 抛出异常
- if (!$path || !is_file($path)) {
- throw new ViewNotFoundException('View not exists:' . $this->viewName($view, true), $path);
+ if (!is_file($path)) {
+ throw new ViewNotFoundException('View not exists:' . ViewName::normalize2tp($view, true), $path);
}
// 记录视图信息
@@ -268,7 +215,7 @@ public function make($view, $data = [], $mergeData = [])
// the caller for rendering or performing other view manipulations on this.
$data = array_merge($mergeData, $this->parseData($data));
- return tap($this->viewInstance($path, $path, $data), function ($view) {
+ return tap($this->viewInstance($view, $path, $data), function ($view) {
$this->callCreator($view);
});
}
@@ -307,38 +254,43 @@ public function fetch(string $view, array $data = [], array $mergeData = [])
* 获取模版所在基础路径
*
* @param string $template 模板文件规则
- * @return string
+ * @return array
*/
- private function parseBasePath(string $template): string
+ private function parseBasePath(string $template): array
{
+ $request = $this->app->request;
+
+ $app = null;
+ $depr = $this->config['view_depr'];
+ $view = $this->config['view_dir_name'] ?: 'view';
+
// 获取视图根目录
if (strpos($template, '@')) {
// 跨应用调用
[$app, $template] = explode('@', $template);
}
- $cutrrentApp = $this->app->http->getName();
+ // 多应用模式
+ if(class_exists('\think\app\MultiApp')) {
- if ($this->config['base_path'] && !isset($app)) {
- $path = $this->config['base_path'];
- } else {
- $appName = isset($app) ? $app : $cutrrentApp;
- $view = $this->config['dir_name'];
+ $appName = is_null($app) ? $this->app->http->getName() : $app;
if (is_dir($this->app->getAppPath() . $view)) {
- $path = isset($app) ? $this->app->getBasePath() . ($appName ? $appName . DS : '') . $view . DS : $this->app->getAppPath() . $view . DS;
+ $path = (is_null($app) ? $this->app->getAppPath() : $this->app->getBasePath() . $appName) . $depr . $view . $depr;
} else {
- $path = $this->app->getRootPath() . $view . DS . ($appName ? $appName . DS : '');
+ $path = $this->app->getRootPath() . $view . $depr . $appName . $depr;
}
+ } else {
+ // 单应用模式
+ $path = $this->app->getRootPath() . $view . $depr;
}
// 设置主题路径
if (!empty($this->config['theme'])) {
- // default 主题备用
- $path .= $this->config['theme'] . DS;
+ $path .= $this->config['theme'] . $depr;
}
- return $path;
+ return [$path, $template];
}
/**
@@ -349,9 +301,10 @@ private function parseBasePath(string $template): string
*/
private function parseTemplate(string $template): string
{
- $depr = $this->config['depr'];
$request = $this->app->request;
- $path = $this->parseBasePath($template);
+ $depr = $this->config['view_depr'];
+
+ [$path, $template] = $this->parseBasePath($template);
if (0 !== strpos($template, '/')) {
$template = str_replace(['/', ':'], $depr, $template);
@@ -374,16 +327,16 @@ private function parseTemplate(string $template): string
$template = Str::snake($request->action());
}
- $template = str_replace('.', DS, $controller) . $depr . $template;
+ $template = str_replace('.', $depr, $controller) . $depr . $template;
} elseif (false === strpos($template, $depr)) {
- $template = str_replace('.', DS, $controller) . $depr . $template;
+ $template = str_replace('.', $depr, $controller) . $depr . $template;
}
}
} else {
$template = str_replace(['/', ':'], $depr, substr($template, 1));
}
- $template = $path . ltrim($template, '/') . '.' . ltrim($this->config['suffix'], '.');
+ $template = $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.');
if (is_file($template)) {
return $template;
@@ -391,18 +344,25 @@ private function parseTemplate(string $template): string
// 未设置主题, 尝试先去default查找
if(empty($this->config['theme'])) {
-
- $default = str_replace(DS .'view'. DS, DS .'view'. DS .'default'. DS, $template);
+ $default = str_replace(
+ $depr .'view'. $depr,
+ $depr .'view'. $depr .'default'. $depr,
+ $template
+ );
if (is_file($default)) {
return $default;
}
}
- // 默认主题不存在模版, 降级删除default主题继续查找
- if (strpos($template, DS .'view'. DS . 'default' . DS) !== false) {
- $default = str_replace(DS .'view'. DS . 'default' . DS, DS .'view'. DS, $template);
+ // 默认主题不存在模版, 降级删除default主题继续查找
+ if (strpos($template, $depr .'view'. $depr . 'default' . $depr) !== false) {
+ $default = str_replace(
+ $depr .'view'. $depr .'default'. $depr,
+ $depr .'view'. $depr,
+ $template
+ );
if (is_file($default)) {
return $default;
@@ -410,11 +370,11 @@ private function parseTemplate(string $template): string
}
// 已设置主题, 但是找不到模版, 尝试降级为default主题
- if (strpos($template, DS .'view'. DS . $this->config['theme'] . DS) !== false) {
-
+ if (strpos($template, $depr .'view'. $depr . $this->config['theme'] . $depr) !== false) {
$default = str_replace(
- DS . $this->config['theme'] . DS,
- DS . 'default' . DS, $template
+ $depr . $this->config['theme'] . $depr,
+ $depr .'default'. $depr,
+ $template
);
if (is_file($default)) {
@@ -425,26 +385,6 @@ private function parseTemplate(string $template): string
return '';
}
- /**
- * Normalize the given template.
- *
- * @param string $name
- * @return string
- */
- private function viewName($template = '', $isRaw = false)
- {
-
- if($isRaw && strpos($template, '/')) {
- return str_replace('/', '.', $template);
- }
-
- if (strpos($template, '.')) {
- $template = str_replace('.', '/', $template);
- }
-
- return $template;
- }
-
/**
* Get the evaluated view contents for the given view.
*
@@ -876,4 +816,9 @@ public function callComposer($view)
public function callCreator($view)
{
}
+
+ public function __call($method, $params)
+ {
+ return call_user_func_array([$this->app->get('blade.compiler'), $method], $params);
+ }
}
diff --git a/src/View/FileViewFinder.php b/src/View/FileViewFinder.php
index d9f5577..94b21ac 100644
--- a/src/View/FileViewFinder.php
+++ b/src/View/FileViewFinder.php
@@ -1,19 +1,12 @@
files = $files;
$this->paths = array_map([$this, 'resolvePath'], $paths);
if (isset($extensions)) {
@@ -102,7 +93,7 @@ protected function findNamespacedView($name)
*/
protected function parseNamespaceSegments($name)
{
- $segments = explode(static::HINT_PATH_DELIMITER, $name);
+ $segments = explode(ViewName::HINT_PATH_DELIMITER, $name);
if (count($segments) !== 2) {
throw new InvalidArgumentException("View [{$name}] has an invalid name.");
}
@@ -125,7 +116,7 @@ protected function parseNamespaceSegments($name)
*/
protected function findInPaths($name, $paths)
{
- if (str_contains($name, '@')) {
+ /*if (str_contains($name, '@')) {
[$app, $name] = explode('@', $name);
$currentApp = app('http')->getName();
@@ -133,11 +124,11 @@ protected function findInPaths($name, $paths)
$paths[] = base_path($app) .'view';
$this->addLocation(base_path($app) .'view');
}
- }
+ }*/
foreach ((array) $paths as $path) {
foreach ($this->getPossibleViewFiles($name) as $file) {
- if ($this->files->exists($viewPath = $path.'/'.$file)) {
+ if (file_exists($viewPath = $path.'/'.$file)) {
return $viewPath;
}
}
@@ -261,7 +252,7 @@ public function addExtension($extension)
*/
public function hasHintInformation($name)
{
- return strpos($name, static::HINT_PATH_DELIMITER) > 0;
+ return strpos($name, ViewName::HINT_PATH_DELIMITER) > 0;
}
/**
@@ -274,16 +265,6 @@ public function flush()
$this->views = [];
}
- /**
- * Get the filesystem instance.
- *
- * @return \Illuminate\Filesystem\Filesystem
- */
- public function getFilesystem()
- {
- return $this->files;
- }
-
/**
* Set the active view paths.
*
diff --git a/src/View/View.php b/src/View/View.php
index fe94c78..18b0959 100644
--- a/src/View/View.php
+++ b/src/View/View.php
@@ -1,10 +1,10 @@
render();
}
-
+
/**
* Get the string contents of the view.
*
@@ -167,7 +168,7 @@ public function render(callable $callback = null)
*/
protected function renderContents()
{
- // We will keep track of the amount of views being rendered so we can flush
+ // We will keep track of the number of views being rendered so we can flush
// the section after the complete rendering operation is done. This will
// clear out the sections for any separate views that may be rendered.
$this->factory->incrementRender();
@@ -177,7 +178,7 @@ protected function renderContents()
$contents = $this->getContents();
// Once we've finished rendering the view, we'll decrement the render count
- // so that each sections get flushed out next time a view is created and
+ // so that each section gets flushed out next time a view is created and
// no old sections are staying around in the memory of an environment.
$this->factory->decrementRender();
@@ -230,7 +231,7 @@ public function renderSections()
* Add a piece of data to the view.
*
* @param string|array $key
- * @param mixed $value
+ * @param mixed $value
* @return $this
*/
public function with($key, $value = null)
@@ -249,7 +250,7 @@ public function with($key, $value = null)
*
* @param string $key
* @param string $view
- * @param array $data
+ * @param array $data
* @return $this
*/
public function nest($key, $view, array $data = [])
@@ -354,7 +355,7 @@ public function offsetGet($key): mixed
* Set a piece of data on the view.
*
* @param string $key
- * @param mixed $value
+ * @param mixed $value
* @return void
*/
public function offsetSet($key, $value): void
@@ -388,7 +389,7 @@ public function &__get($key)
* Set a piece of data on the view.
*
* @param string $key
- * @param mixed $value
+ * @param mixed $value
* @return void
*/
public function __set($key, $value)
@@ -422,7 +423,7 @@ public function __unset($key)
* Dynamically bind parameters to the view.
*
* @param string $method
- * @param array $parameters
+ * @param array $parameters
* @return \Illuminate\View\View
*
* @throws \BadMethodCallException
diff --git a/src/View/ViewException.php b/src/View/ViewException.php
index 1c9b7dc..693ab1a 100644
--- a/src/View/ViewException.php
+++ b/src/View/ViewException.php
@@ -1,4 +1,5 @@
getName();
+ // if ($namespace != $app) {}
+
+ return $namespace . $delimiter . str_replace('/', '.', $name);
}
/**
* Normalize the given template.
*
* @param string $name
+ * @param bool $raw
* @return string
*/
- public static function normalize2($template = '', $isRaw = false)
+ public static function normalize2tp($template = '', $raw = false)
{
- if($isRaw && strpos($template, '/')) {
+ if($raw && strpos($template, '/')) {
return str_replace('/', '.', $template);
}
diff --git a/src/facade/View.php b/src/facade/View.php
index 8766dd9..e062655 100644
--- a/src/facade/View.php
+++ b/src/facade/View.php
@@ -10,7 +10,6 @@ class View extends Facade
{
protected static function getFacadeClass()
{
- // return 'blade.compiler';
return 'blade.view';
}
}
\ No newline at end of file
diff --git a/src/helper.php b/src/helper.php
index ef89406..a6d1b12 100644
--- a/src/helper.php
+++ b/src/helper.php
@@ -1,35 +1,10 @@
make($view, $data, $mergeData);
- }
-}
-
-if (! function_exists('blade')) {
+if (! function_exists('view')) {
/**
* Get the evaluated view contents for the given view.
*
@@ -38,7 +13,7 @@ function v($view = null, $data = [], $mergeData = [])
* @param array $mergeData
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
- function blade($view = null, $data = [], $mergeData = [])
+ function view($view = null, $data = [], $mergeData = [])
{
$factory = app('blade.view');
@@ -58,7 +33,7 @@ function blade($view = null, $data = [], $mergeData = [])
*/
function csrf_field(string $name = '__token__', string $type = 'md5'): string
{
- return '';
+ return '';
}
}