oss-gate/workshop

OSS Gate Workshop: php-kagawa: 2024-05-12: akinoriakatsuka: laravel/framework: Work log

akinoriakatsuka opened this issue · 31 comments

This is a work log of a "OSS Gate workshop".
"OSS Gate workshop" is an activity to increase OSS developers.
Here's been discussed in Japanese. Thanks.

作業ログ作成時の説明

以下のテンプレートを埋めてタイトルに設定します。埋め方例はスクロールすると見えてきます。

OSS Gate Workshop: ${LOCATION}: ${YEAR}-${MONTH}-${DAY}: ${ACCOUNT_NAME}: ${OSS_NAME}: Work log

タイトル例↓:

OSS Gate Workshop: Tokyo: 2017-01-16: kou: Rabbit: Work log

OSS Gateワークショップ関連情報

  • スライド:ワークショップの進行に使っているスライドがあります。
  • チャット:OSS開発に関することならなんでも相談できます。ワークショップが終わった後もオンラインで相談しながら継続的にOSSの開発に参加しましょう!
  • シナリオ:ワークショップの目的・内容・進め方の詳細が書いています。
  • 過去のビギナーの作業ログ:他の人の作業ログから学べることがいろいろあるはずです。

再現を試みる

composer create-project laravel/laravel example-app

インストールはOK

php artisan serve

ローカルホストでアクセスできるようになった

routes/console.php

デフォルトでconsole.phpに記載があった。ので、listを表示してみた。

$ php artisan schedule:list

  0 * * * *  php artisan inspire ............................................................................................................................ Next Due: 31 minutes from now

これってどこに書くのかな?調べてみる。

$schedule->command('emails:send')
      ->hourly()
      ->between('7:00', '22:00');

Scheduleファサードでconsole.phpに書いたらよさそう

Schedule::command('inspire')
    ->hourly()
    ->between('7:00', '22:00');

再現した

$ php artisan schedule:list

  0 * * * *  php artisan inspire  Next Due: 19 minutes from now
  0 * * * *  php artisan inspire  Next Due: 19 minutes from now
Schedule::command('inspire')
    ->hourly()
    ->between('7:00', '22:00');
Schedule::command('inspire2')
    ->hourly()
    ->cron('0 7-22 * * *');

cronの形式で渡してやればちゃんと出る

0 *    * * *  php artisan inspire ......................................................................................................................... Next Due: 58 minutes from now
0 7-22 * * *  php artisan inspire2 ........................................................................................................................... Next Due: 3 hours from now

次は、どこで実装されているかみる

laravel/frameworkをクローンしてきた

とりあえず、実装見る前にテスト流してみる

Time: 01:24.133, Memory: 316.00 MB

OK, but some tests were skipped!
Tests: 10019, Assertions: 27333, Skipped: 530.

artisanってどこ?
php artisanで実行できるってことは、example-appの直下のartisanがそれ?

example-app/artisan

// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
    ->handleCommand(new ArgvInput);

それっぽい

handleCommandを見る

require_once __DIR__.'/bootstrap/app.php'の戻り値のインスタンスのhandleCommand。
app.phpの戻り値はApplicationクラスのインスタンス

use Illuminate\Foundation\Application;

これのhandleCommand

example-app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php

    /**
     * Handle the incoming Artisan command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @return int
     */
    public function handleCommand(InputInterface $input)
    {
        $kernel = $this->make(ConsoleKernelContract::class);

        $status = $kernel->handle(
            $input,
            new ConsoleOutput
        );

        $kernel->terminate($input, $status);

        return $status;
    }

kernelのhandleなので、handleを見てみる

kernelは$this->makeでできる

        $kernel = $this->make(ConsoleKernelContract::class);

        var_dump($kernel);

メンターさんのアドバイスでこちらで該当箇所を探してみた。

grep -lr schedule:list ./
.//src/Illuminate/Console/Scheduling/ScheduleListCommand.php

このファイルであることがわかった
framework/src/Illuminate/Console/Scheduling/ScheduleListCommand.php

var_dumpで、$eventsを見てみる

object(Illuminate\Support\Collection)#556 (2) {
  ["items":protected]=>
  array(1) {
    [0]=>
    object(Illuminate\Console\Scheduling\Event)#460 (22) {
      ["command"]=>
      string(55) "'/usr/local/Cellar/php/8.3.6/bin/php' 'artisan' inspire"
      ["expression"]=>
      string(9) "0 * * * *"
      ["repeatSeconds"]=>
      NULL

expressionが怪しい?

src/Illuminate/Console/Scheduling/Event.php
に見にいく

40行目にpublic $expressionがある!

$expression に代入しているところ見つからない…

おつかれさまでした!

ワークショップの終了にともないissueを閉じますが、このまま作業メモとして使っても構いません 👌

ワークショップの感想を集めています!

ブログなどに書かれた際は、このページへリンクの追加をお願いします 🙏

またの参加をお待ちしています!

$eventsを吐かせてみて、中の情報がどうなっているか調べる

  ["expression"]=>
  string(9) "0 * * * *"

        ["startTime"]=>
        object(Illuminate\Support\Carbon)#462 (20) {

          ["date"]=>
          string(26) "2024-05-12 07:00:00.000000"

        ["endTime"]=>
        object(Illuminate\Support\Carbon)#466 (20) {

          ["date"]=>
          string(26) "2024-05-12 22:00:00.000000"

上記から、終了時間と開始時間は取れてるっぽいが、expressionの計算がうまく行ってないっぽい

issueで問題になっているbetweenメソッドについてはtestケースがない!

Eventのexpressionがどうやって計算されているかを確認する

$expressionを代入する部分ない。traitかな?

traitビンゴ!
src/Illuminate/Console/Scheduling/ManagesFrequencies.php

以下の実装見る限り
$event->cron($expression)
の形で外から与えられるっぽい。

/**
 * The Cron expression representing the event's frequency.
 *
 * @param  string  $expression
 * @return $this
 */
public function cron($expression)
{
    $this->expression = $expression;

    return $this;
}

ので、次は ->cron( を探す。

twiceDailyはうまくcron出せてるので、この実装をまねしてみよう

tests/Console/Scheduling/FrequencyTest.php

こちらにもbetweenのテストはない

メソッド追っかけ

    /**
     * Schedule the event to run twice daily.
     *
     * @param  int  $first
     * @param  int  $second
     * @return $this
     */
    public function twiceDaily($first = 1, $second = 13)
    {
        return $this->twiceDailyAt($first, $second, 0);
    }
    /**
     * Schedule the event to run twice daily at a given offset.
     *
     * @param  int  $first
     * @param  int  $second
     * @param  int  $offset
     * @return $this
     */
    public function twiceDailyAt($first = 1, $second = 13, $offset = 0)
    {
        $hours = $first.','.$second;

        return $this->hourBasedSchedule($offset, $hours);
    }
  /**
    * Schedule the event to run at the given minutes and hours.
    *
    * @param  array|string|int  $minutes
    * @param  array|string|int  $hours
    * @return $this
    */
   protected function hourBasedSchedule($minutes, $hours)
   {
       $minutes = is_array($minutes) ? implode(',', $minutes) : $minutes;

       $hours = is_array($hours) ? implode(',', $hours) : $hours;

       return $this->spliceIntoPosition(1, $minutes)
                   ->spliceIntoPosition(2, $hours);
   }

ここまできてやっとcronあった。

   * Splice the given value into the given position of the expression.
     *
     * @param  int  $position
     * @param  string  $value
     * @return $this
     */
    protected function spliceIntoPosition($position, $value)
    {
        $segments = preg_split("/\s+/", $this->expression);

        $segments[$position - 1] = $value;

        return $this->cron(implode(' ', $segments));
    }

明日はbetween掘ってみてcronに辿り着くかやってみる