imanghafoori/laravel-self-test

此包已被 弃用 并不再维护。作者建议使用 imanghafoori/laravel-microscope 包代替。

自动测试您的laravel应用程序

v1.0.369 2024-09-20 22:25 UTC

This package is auto-updated.

Last update: 2024-09-21 11:43:03 UTC


README

在它们咬人之前找出错误

microscope_header

用❤️为懒惰的Laravel开发者构建 ;)

为什么重复旧错误,如果还有这么多新的错误要提交呢?

(伯特兰·罗素)

让您的眼睛休息一下,我们将为您检测和修复它们。

Packagist Stars Required Laravel Version Required PHP Version Latest Version on Packagist Quality Score Total Downloads Today Downloads tests Imports

要知道的关键事项

  • 它旨在比phpstorm和其他IDE更聪明地找到错误。
  • 它旨在 理解laravel运行时 和魔法。
  • 它不会显示愚蠢的错误,所有报告的案例都是真实的错误。
  • 即使你已经为你的应用程序编写了大量的测试, 你仍然可能需要这个
  • 它可以重构你的代码,通过自动应用 早期返回
  • 它从头开始编写,以产生尽可能的 高性能

🎞️ 视频教程 在这里

⭐ 你的星星让我们做得更多

如果你认为这个包很有用,并且你想鼓励维护者继续工作,只需按下星号按钮来宣布你的意愿。

星星观察者

⬇️ 安装

您可以通过Composer 安装 此包

composer require imanghafoori/laravel-microscope --dev

您还可以 发布 配置文件

php artisan vendor:publish --provider="Imanghafoori\LaravelMicroscope\LaravelMicroscopeServiceProvider"

💎 使用

有用的命令

您可以使用以下命令运行:

较少使用的命令

全局辅助函数

此外,您将能够访问一些全局辅助函数

microscope_dd_listeners($event);

如果您想知道监听器是什么以及它们在哪里,您可以在 bootregister 方法中调用 microscope_dd_listeners(MyEvent::class);。它就像正常的 dd(...); 一样工作,意味着程序将在该点停止运行。

📖 命令做什么?

让我们从

php artisan search_replace {--name=pattern_name} {--tag=some_tag} {--file=partial_file_name} {--folder=partial_folder_name}

这是一个智能且非常强大的搜索/替换功能,可以真正为您节省时间。

1️⃣ 定义模式

如果您第一次运行artisan search_replace命令,它将在项目的根目录中创建一个search_replace.php文件。然后,您可以在该文件中定义您的模式。

示例

让我们定义一个模式,用?-> php 8 null safe 运算符替换全局助手函数optional()

return [
    'optional_to_nullsafe' => [
        'search' => '"<global_func_call:optional>"("<in_between>")->',
        'replace' => '"<2>"?->',
        // 'tag' => 'php8,refactor',
        // 'predicate' => function($matches, $tokens) {...},
        // 'mutator' => function($matches) {...},
        // 'post_replace' => [...],
        // 'avoid_result_in' => [...],
        // 'avoid_syntax_errors' => false,
        // 'filters' => [...],
    ]
];
  • 这里键optional_to_nullsafe是您的模式的“唯一名称”。(您可以通过运行php artisan search_replace --name=optional_to_nullsafe来定位您的模式)
  • 搜索模式有一个"<in_between>"占位符,它捕获括号对之间的所有内容。
  • replace块中,我们将第一个占位符捕获的内容替换为"<1>"。如果我们有更多的占位符,我们可以有"<2>"等。
  • 在标签块中,我们可以将一些标签作为字符串数组或以逗号分隔的字符串提及,并通过--tag标志定位它们:php artisan search_replace --tag=php8

2️⃣ 占位符:

这里是一个您可以使用的占位符的完整列表

如果需要,您也可以定义自己的关键字!

您只需定义一个新的关键字类,并将类路径附加到Finder::$keywords[] = MyKeyword::class属性末尾。就像默认关键字一样。

示例

1️⃣ 假设您只想找到包含“todo:”单词的“注释”。

 'todo_comments' => [
        'search' => '<comment>',
        'predicate' => function($matches) {    //   <====  here we check comment has "todo:"
            $comment = $matches[0]; // first placeholder value
            $content = $comment[1]; // get its content
            
            return Str::contains($content, 'todo:') ? true : false;
        },
]

注意 如果您没有提及'replace'键,它只会搜索并报告给您。

2️⃣ 好的,现在假设您想从注释中删除“todo:”单词

 'remove_todo_comments' => [
    'search' => '<comment>',      //   <=== we capture any comment
    'replace' => '<1>',

    'predicate' => function($matches) {
        $comment = $matches[0]; // first matched placeholder
        $content = $comment[1];

        return Str::contains($content, 'todo:') ? true : false;
    },

    'mutator' => function ($matches) {       //  <=== here we remove "todo:"
        $matches[0][1] = str_replace('todo:', '', $matches[0][1]);

        return $matches;
    }
]

转换为: // todo: refactor code Into: // refactor code

3️⃣ 突变:在突变中,您可以在将它们替换到结果中之前,自由地根据需要操作$matched值。您也可以提及一个静态方法而不是一个函数,如下所示:[MyClass::class, 'myStaticMethod']

3️⃣ 假设您想在数组中缺少时为Let's元素添加可选逗号。

    'enforce_optional_comma' => [
        'search' => '<white_space>?]',
        'replace' => ',"<1>"]',
        'avoid_syntax_errors' => true,
        'avoid_result_in' => [
           ',,]',
           '[,]',
           '<var>[,]'
       ],
    ]

在这种情况下,我们的模式并不非常准确,并且在某些情况下可能会导致语法错误。因此,我们打开php语法验证器来检查结果,但这会给我们带来性能惩罚!!!为了排除PHP的使用,我们已提到avoid_result_in,这样如果它们出现在结果中,则会跳过。

  • 注意:在"<white_space>?"中的?表示这是一个optional占位符。

如果您想看到一个不需要任何语法检查的更好的模式,请尝试这个

'enforce_optional_comma' => [
       'search' => '<1:any><2:white_space>?[<3:until_match>]',
       'replace' => '<1><2>[<3>,]',
       'avoid_result_in' => [
           ',,]',
           '[,]'
       ],
       'predicate' => function ($matches) {
           $type = $matches['values'][0][0];

           return $type !== T_VARIABLE && $type !== ']';
       },
       'post_replace' => [
           '<1:white_space>,]' => ',<1>]'
       ]
],

这个更复杂,但速度更快。(因为它不需要php语法验证器)

  • 在这里,'post_replace'是一个仅在结果上应用,以细化结果的模式,而不是在整个文件上。

  • 您可以选择性地注释占位符(如上<1:any>),并用数字标记,这样您在替换时就可以知道哪个对应哪个。

4️⃣ 过滤器:

目前,显微镜只提供了两个内置过滤器:is_sub_class_ofin_array

你能猜出这个模式在做什么吗?!

 'mention_query' => [
      'search' => '<1:class_ref>::<2:name>'
      'replace' => '<1>::query()-><2>',
      'filters' => [
          1 => [
              'is_sub_class_of' => \Illuminate\Database\Eloquent\Model::class
          ],
          2 => [
              'in_array' => 'where,count,find,findOrFail,findOrNew'
          ]
      ]
  ]

它将这些转换为

User::where(...)->get();

\App\Models\User::find(...);

这些

User::query()->where(...)->get();

\App\Models\User::query()->find(...);
  • 这里的过滤器确保捕获的类引用是Laravel模型,并且方法名称是列表中提到的名称之一。

所以它不会干扰像这样的事情

User::all();            // The `all` method can not be preceded by `query`

UserRepo::where(...);   /// UserRepo is not a model
  • 这是您永远不能通过正则表达式做到的事情。

5️⃣ 捕获php“语句”:

假设我们想选择PHP v7.4箭头函数

'fn' => [
    'search' => 'function (<in_between>)<until>{ return <statement>; }',
    'replace' => 'fn (<1>) => <3>',
    'tags' => 'php74,refactor',
]

在这个例子中,我们在函数体中只提到一个“语句”。因此,如果它遇到有两个或更多语句的函数,它将忽略。

$closure = function ($a) use ($b) {
    return $a + $b;
};

// will become:
$closure = fn($a) => $a + $hello;

但这没有被捕获

$closure = function ($a) {
    $a++;
    return $a + $b;
};

6️⃣ <statement><until> 的区别;

它们看起来非常相似,但在一个重要的情况下,你不能使用 <until> 来正确地覆盖它!

$first = $a + $b;

$second = function ($a) {
    $a++;

    return $a;
};

如果我们这样定义我们的模式

return [
    'pattern_name' => [
        'search' => '<var> = <until>;',   
    ]
];

对于 $c = $a + $b;,它们的行为相同,但对于第二个 "<until>";,它不会捕获整个闭包,而会在到达 $a++; 时停止,这是一个问题。

但如果你将模式定义为:'<var> = <statement>',它就会足够智能,能够捕获闭包定义末尾的正确分号,整个闭包都会被捕获。

7️⃣ 捕获全局函数调用:

假设你想要在生产之前消除所有 dd(...)dump(...)

return [
    'remove_dd' => [
        'search' =>  "'<global_func_call:dd,dump>'('<in_between>');", 
        'replace' => ''
    ]
];

这不会捕获以下情况

$this->  dd('hello');          // is technically a method call
User::   dd('I am static');    // is technically a static method call
new      dd('I am a class');  // here "dd" is the name of a class.
   

但会检测并删除具有任何参数的真实全局 dd() 调用。

dd(                // <=== will be detected, even if the pattern above is written all in one line.
   auth('admin')
        ->user()->id   
);
    
    
\dd(1);
dd(1);
dump(1);
    

8️⃣ 重复模式:

假设我们想要重构

User:where('name', 'John')->where('family', 'Dou')->where('age', 20)->get();

User:where([
    'name' => 'John',
    'family' => 'Dou',
    'age'=> 20,
])->get();

好的,那么模式会是什么样子呢?!

"group_wheres" => [
       
       'search' => '<1:class_ref>::where('<2:str>', '<3:str>')'<repeating:wheres>'->get();'
       
       'replace' => '<1>::where([
           <2> => <3>,
           "<repeating:1:key_values>"])->get();',

       'named_patterns' => [
           'wheres' => '->where(<str>, <str>)<white_space>?',
           'key_values' => '<1> => <2>,<3>',
       ]
   ]

棒吧?

可能性是无限的,天高任鸟飞...

php artisan check:early_returns

这将扫描所有你加载的 Psr-4 类,并应用早期返回规则来简化你的函数和循环。例如

<?php

foreach ($products as $product) {
    if ($someCond) {
        // A lot of code 1
        // A lot of code 1
        // A lot of code 1
        // A lot of code 1
        // A lot of code 1
        if ($someOtherCond) {
            // A lot more code 2
            // A lot more code 2
            // A lot more code 2
            // A lot more code 2 
            // A lot more code 2
            //
        } // <--- closes second if
    } // <--- closes first if
}

将被发现并转换为

<?php

foreach ($products as $product) {
    if (! $someCond) {
        continue;
    }
    
    // A lot of code 1
    // A lot of code 1
    // A lot of code 1
    // A lot of code 1
    // A lot of code 1

    if (! $someOtherCond) {
        continue;
    }
 
    // A lot more code 2
    // A lot more code 2
    // A lot more code 2
    // A lot more code 2 
    // A lot more code 2
}

同样的规则也适用于函数和方法,但使用 return

<?php

if ($cond1) {
    if ($cond2) {
        ....       
    }
}

// we get merged into:

if ($cond1 && $cond2) { 
    ...  
}
  • 它还支持类似 ruby 的 if():/endif; 语法;
<?php

if ($var1 > 1):
    if ($var2 > 2):
        echo 'Hey Man';
    endif;
endif;

// Or if you avoid putting curly braces...
if ($var1 > 1)
    if ($var2 > 2)
        echo 'Hey Man';

虽然这种重构是安全的,并且保证与之前做同样的事情,但在尝试此功能之前,请务必提交所有更改,以防出现奇怪的错误或类似情况。

php artisan check:psr4

  • 它检查 composer.json 文件中定义的所有 psr4 自动加载,并遍历所有类以获得正确的命名空间,根据 PSR-4 标准。
  • 它根据 PSR-4 规则自动修复命名空间。
  • 它还检查对旧命名空间的引用,并将它们替换为新命名空间。

php artisan check:generate

你创建一个空文件,我们根据命名约定来填充它。

如果你在运行此命令后创建一个空的 .php 文件,文件名以 ServiceProvider.php 结尾:1 - 它将被填充模板和正确的 Psr-4 命名空间。2 - 它将被附加到 config/app.php 文件中的 providers 数组。

php artisan check:imports

  • 它检查所有导入(use 语句)是否有效,并报告无效的导入。
  • 它自动修复某些引用,确保类名没有歧义。
  • 它理解 Laravel 别名类,因此 use Request; 是有效的。

php artisan check:bad_practices

  • 它检测不良实践,如 config 文件外部的 env() 调用。

php artisan check:routes

  • 它检查你的路由是否引用有效的控制器类和方法。
  • 它检查所有控制器方法都有有效的类型提示。
  • 它扫描 route()redirect()->route()\Redirect::route() 是否引用有效的路由。
  • 它会报告没有路由指向它们的控制器公开方法。换句话说,检测到“死控制器”。

php artisan check:compact

  • 在 PHP 7.3 中,如果你对一个不存在的变量进行“compact”,你会得到一个错误,因此此命令会检查整个项目中的错误 compact() 调用,并向你报告应该删除哪些参数。

php artisan check:blade_queries

  • Blade 文件不应包含数据库查询。我们应该将它们移回控制器并传递变量。此命令在所有 blade 文件中搜索 Eloquent 模型DB 查询构建器,并在找到任何内容时显示它们。

php artisan check:extract_blades

  • 如果您想提取部分视图并包含它,例如:@include('myPartials.someFile')

您可以在blade文件中使用{!! extractBlade('myPartials.someFile') !!}来指定要包含的视图的起始/结束行路径/名称

 <html>
      
      {!! extractBlade('myPartials.head') !!}
          <head>...</head>
      {!! extractBlade() !!}

      
      {!! extractBlade('myPartials.body') !!}
          <body>...</body>
      {!! extractBlade() !!}
      
 </html>

执行php artisan check:extract_blades后,它会变为

<html>
    @include('myPartials.head')
    @include('myPartials.body')
</html>

此外,它还会创建

  • resources/views/myPartials/head.blade.php
  • resources/views/myPartials/body.blade.php

并将相应的内容放入其中。

  • 它还与模块化Laravel应用程序的命名空间视图兼容。因此此语法将工作:'MyMod::myPartials.body'

php artisan check:action_comments {--file=SomeFile.php}

  • 这将在控制器操作中添加注释,以便您知道哪个路由指向当前控制器操作。
  • 您可以使用--file=选项来缩小扫描的文件。

php artisan pp:route

  • 首先,您必须在路由文件中放入此代码:microscope_pretty_print_route('my.route.name');
  • 您还可以将Controller@method语法传递给函数。
  • 您可以多次调用它来美化打印多个路由。

php artisan check:views

  • 它扫描您的代码,找到view()View::make(),并报告它们是否引用了错误的文件。
  • 它扫描blade文件中的@include()@extends(),并报告它们是否引用了错误的文件。

此外,它可以检测从控制器传递到视图中的未使用变量,例如:view('hello', [...]);为了做到这一点,您必须打开页面并在浏览器中查看日志文件,以看到如下信息

local.INFO: Laravel Microscope: The view file: welcome.index-1 at App\Http\Controllers\HomeController@index has some unused variables passed to it:   
local.INFO: array ('$var1' , '$var2');

请注意,一些变量是从视图构建器而不是控制器传递到视图的。在检测未使用变量时也会考虑这些变量。

php artisan check:events

例如,考虑以下内容

Event::listen(MyEvent::class, '\App\Listeners\MyListener@myMethod');

1 - 它检查\App\Listeners\MyListener类路径是否有效。

2 - 它检查myMethod方法是否存在于MyListener类上

3 - 它检查myMethod方法是否在其签名中有正确的类型提示(如果有),例如

public function myMethod(OtherEvent $e) // <---- notice type-hint here
{
    //
}

这是一个有效但错误的类型提示,并将向您报告。很酷,不是吗 ??!

  • 请注意,您如何设置事件监听器并不重要,

1- 在EventServiceProvider中,

2- 通过Event::listen外观,

3- 通过订阅器类...或任何其他方式。错误将被找到。 :)

php artisan check:gates

它检查所有已定义的大门的有效性,确保它们引用有效的类和方法。

它还检查策略定义是否有效。

Gate::policy(User::class, 'UserPolicy@someMethod');
Gate::define('someAbility', 'UserGate@someMethod');

1 - 它检查User类路径是否有效。

2 - 它检查UserPolicy类路径是否有效。

3 - 它检查someMethod方法是否存在。

php artisan check:dynamic_where

  • 它寻找类似whereFamilyName('...')where('family_name', '...')的"动态where"方法。

php artisan enforce:query

  • 它调用Eloquent查询链上的静态query方法,以便IDE可以理解Eloquent。

  • 例如,将User::where(...转换为User::query()->where(...

php artisan check:dead_controllers

  • 我们可以找到没有任何路由的控制器。

php artisan check:generic_docblocks {--folder=app/Models} {--file=SomeFile.php}

  • 删除Laravel的DocBlocks。
  • 您可以使用--folder=--file=选项来缩小扫描的文件夹。

php artisan enforce:helper_functions {--folder=app/Models} {--file=SomeFile.php}

  • 将Laravel外观转换为辅助函数。
  • 您可以使用--folder=--file=选项来缩小扫描的文件夹。

php artisan list:models {--folder=app/Models}

  • 它在项目中搜索并列出模型类。
  • 您可以使用--folder=选项来缩小扫描的文件夹。

更多功能将很快添加。 ;)

致谢

许可

MIT许可(MIT)。请参阅许可文件以获取更多信息。

🙋 贡献

如果您发现任何问题或有更好的方法来做某事,请随时提出问题或发起拉取请求。如果您在开源项目中使用laravel-microscope,请在README.md文件中创建一个拉取请求,提供其URL作为示例应用程序。

❗ 安全性

如果您发现任何与安全性相关的问题,请发送电子邮件至 imanghafoori1@gmail.com,而不是使用问题跟踪器。

作者的其他作品

Laravel HeyMan

💎 允许我们编写易于表达授权、验证和认证的代码。

Laravel Terminator

💎 一个最小化但功能强大的包,允许您重构您的控制器。

Laravel AnyPass

💎 它允许您仅在本地环境中使用任何密码登录。

A man will never fail unless he stops trying.

Albert Einstein

❤️ 贡献者

本项目能够存在,归功于所有贡献者。[贡献者].

⭐ 星级历史

Star History Chart