20200913のPHPに関する記事は13件です。

VSCodeでphpをデバッグする

環境
初書:2020/09/13
PC:macOS 10.15.6
php 7.4.9
xdebug 2.9.6

前置き

PHPでもデバッグしたい!という事で、デバッグする方法を探してみた。

前提

peclが利用可能な環境・vscodeがインストール済み

debugをインストールする

PHP debug

vscodeの拡張機能から、PHP Debugをインストールする
(publisher:Felix Becker。他の人のでも動くかは未検証)

xdebug

ターミナルから、

% pecl install xdebug

を実行し、xdebugをインストール
% pecl listを実行し、xdebugが表示されればインストールは完了

php.iniを設定する

% vim /usr/local/etc/php/7.4/php.ini

(※phpのバージョン及びphp.iniの場所によって座標は変更する。)
を実行し、以下のコードを追加する(一番上に書き足しても動く)

zend_extension="/usr/local/Cellar/php/7.4.9/pecl/20190902/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_port = 8080

(zend_extensionには、xdebugをインストールした際にInstalling '(略)'と記載されているので、その値。
xdebug.remote_portには、普段phpを立てる際のポート番号を設定する)

2020/09/15 追記
各項目の説明をしてくれているサイトをがあったので:[PHP] Xdebug のリモートデバッグ、理解していますか?

vimの操作覚えてないのでメモ:Vim初心者のための基本的な操作方法のまとめ

vscodeを設定する

vscodeのデバッグ画面(command + shift + D)を開き、上部にある歯車のマークをおし、launch.jsonを開く
"port"欄を、xdebug.remote_portと同じ値にする

phpを実行する

設定は終わったので、動作するか確認する
vscodeのphpファイルで適当にブレークポイントを入れ、下記を実行する

% php -S XXXX:8080

(XXXX及び8080は自身の環境に応じて変更する)
その後、該当ページにアクセスし、ブレークポイントで止まれば完了する。

終わりに

こちらの環境では、ブレークポイントで止まるものの、何行目で止まっているのかの表示がされないのでもしかしたらどこかで設定ミスがあるかもしれない。
また、デバッグの設定は過去に一度断念しているらしく、一部設定がしてあったり、インストールされていたりしたので、もしかしたら上記設定以外にも必要かもしれないが、覚えていない。。

2020/09/15 追記
xdebugは、var_dumpの表示方法を変えるシステムが存在しているらしく、出力が少し変わった。
もし気になる場合はautostartにしない方がいいかもしれない。

参考サイト:

Visual Studio CodeとXdebugでPHPのデバッグ環境を構築する
[XAMPP Mac]VSCodeでPHPのXdebugを使いデバッグ!しかしブレークポイントが止まらない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHPメモ】in_arrayで重複をチェック

0.目的

自分用のメモ

1.重複文字問題

(1)112233が入力の時
⇒123
(2)233342213が入力の時
⇒2341
のように出力するプログラムを作る。

2.メモ

in_array(検索対象文字列,配列):配列の中に検索対象文字列が存在するか確認する関数、存在する場合true,存在しない場合false

3.コード

※paiza.IOで動作確認

<?php

    $in1 = fgets(STDIN);
    $in1=trim($in1);
    $len1=strlen($in1);
    //重複を取り除いた文字配列
    $arr_dict=[];

    for($i=0;$i<$len1;$i++){
        //$in1の1文字が$arr_dict配列に含まれない時
        if(!in_array(substr($in1,$i,1),$arr_dict)){
            $ans=substr($in1,$i,1);
            $arr_dict[]=$ans;
            echo $ans;
        } 
    }

?>

入力

1234904743521627489064953371426326487539813121258593765158596901918

出力

1234907568
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

array_push の返り値

array_push の返り値は処理後の配列そのものではなく、配列の要素数なので、書き方には気を付けなければいけません。

example.php
$ex_array = array();
array_push($ex_array, 'youso');
//$ex_array = array_push($ex_array, 'youso1'); ではエラーになる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 8 の新機能!マイグレーションスカッシングで肥大化したマイグレーションファイルを1つにまとめる

大規模または長期間運用されているアプリケーションでは、
数年以上前に作成されたマイグレーションファイルが膨大な数になってきます。

データベーステストを行っているプロジェクトでは、マイグレーションを適用するのに時間がかかりテストの速度が低下する懸念があります。
これの解決策として、Laravel 8ではマイグレーションスカッシング(スキーマダンプ)と呼ばれる新機能が追加されました。

mysqldump まはた pgdump を使用して現在のスキーマの状態からダンプを作成します。
(MySQL、PostgreSQL、SQLiteデータベースのみサポート)

公式ドキュメント・ソース

マイグレーションファイルを確認

初期状態のままですが...下記のマイグレーションファイルがあるとします。

$ ls -1 database/migrations
2014_10_12_000000_create_users_table.php
2014_10_12_100000_create_password_resets_table.php
2019_08_19_000000_create_failed_jobs_table.php

スキーマダンプ コマンド

$ php artisan schema:dump

--prune オプションを付けると、既存のマイグレーションファイルの削除が行われます。

$ php artisan schema:dump --prune

database/schema/mysql-schema.sql が生成されます。
/database/schema/<DB_CONNECTION>-schema.mysql

database/schema/mysql-schema.sql
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `batch` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_resets` (
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  KEY `password_resets_email_index` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email_verified_at` timestamp NULL DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

INSERT INTO `migrations` VALUES (1,'2014_10_12_000000_create_users_table',1);
INSERT INTO `migrations` VALUES (2,'2014_10_12_100000_create_password_resets_table',1);
INSERT INTO `migrations` VALUES (3,'2019_08_19_000000_create_failed_jobs_table',1);

migrations 以外のテーブルのデータはダンプされず、テーブル構成のみダンプされるみたいです。(マイグレーションなのにデータまでダンプされたら困りますが...)

マイグレーションを実行する

$ php artisan migrate:reset
$ php artisan migrate
Loading stored database schema: /Users/ucan-lab/laravel-love/backend/database/schema/mysql-schema.sql
Loaded stored database schema. (128.57ms)
Nothing to migrate.

mysql-schema.sql が読み込まれて、既存のマイグレーションファイルは無視されてます。

スキーマダンプのメリット

前述の通り、データベーステストが高速化されるメリットがあります。

また、スキーマダンプはマイグレーションファイルではなく mysqldump コマンドを使って現在のデータベースからダンプファイルを作成されています。
本来の用途とは違うかもしれませんが、マイグレーションファイルでデータベースが管理されていないプロジェクトからLaravelに移行する際はとても役に立つ機能ではないでしょうか。

使ってみた感想

mysqldump コマンドをLaravelで実行しているので、Dockerでやろうとするとmysqldumpコマンドをコンテナ内で使えるようにしてあげる必要があるし、debianのdefault-mysql-clientはMariaDBに統合されたようでLaravelで実行しているオプションが存在していなかったりとDockerで頑張ろうとすると手間かもしれません...今回はローカルで試してみました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】デバッグ

PHPについて学習内容を備忘録としてまとめます。

デバッグについて

開発中にエラーが発生した際に、デバッグをすると思います。
その中でも私がデバッグ方法を記載します。

var_dump

コードの中で値を確認する際に利用することができます。
例えば、下記のような従業員がログインするコードで見ていきます。

#staff_login.php

<form method="post"action="staff_login_cheak.php">
スタッフコード<br />
<input type="text" name="code"><br />
パスワード<br />
<input type="password" name="pass"><br />

#staff_mypage.php

$staff_code=$_POST['code'];
$staff_pass=$_POST['pass'];

var_dump($staff_code);

※本当は間にDBから該当のユーザーを取得する処理がありますが、省略しています。
このように記載すると、staff_login_cheak.phpにスタッフコードフォームで記入したコード番号が表示されます。

image.png

↓ ログイン

image.png

デバッグを別ファイルで管理

ajaxなどでデバッグを実施する際に、上記のやり方だと表示されません。
バックエンドの処理なので一度デバッグ情報をファイルに出力する方法をとります。

方法としては_debug()という関数を作成し、引数に確認したい値等を入れると表示される処理を行います。

#_debug()関数を作成
  function _debug( $data, $clear_log = false ) {
    $uri_debug_file = $_SERVER['DOCUMENT_ROOT'] . '/debug.txt';
    if( $clear_log ){
      file_put_contents($uri_debug_file, print_r($data, true));
    }
    file_put_contents($uri_debug_file, print_r($data,true), FILE_APPEND);
    }

上記関数は自分がわかるところで作成してください。
それではいいね機能のajaxファイルにて、いいねした際のデバッグを見ていきます。

if(isset($_POST)){

  $current_user = get_user($_SESSION['staff_code']);
  $page_id = $_POST['page_id'];
  $post_id = $_POST['post_id'];

  $profile_user_id = $_POST['page_id'] ?: $current_user['id'];

  if(check_favolite_duplicate($current_user['code'],$post_id)){
    $action = '解除';
    $sql = "DELETE
            FROM favorite
            WHERE :user_id = user_id AND :post_id = post_id";
  }else{
    $action = '登録';
    $sql = "INSERT INTO favorite(user_id,post_id)
            VALUES(:user_id,:post_id)";
  }
 _debug($action);  //ここを追加

いいねボタンを押した結果、設定したdebug.txtにて$actionの値が入っていればデバッグ成功です。

image.png
↓いいねボタンをクリック
image.png

今度はいいね解除ボタンをクリック
image.png

image.png

解除が追記されている。
_debug()関数は_debug('',true)と定義するとdebug.txtが空になるように作成されています。

参考URL

https://pisuke-code.com/php-how-to-debug-by-log-file/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでサブドメイン対応(さくらレンタルサーバ)

やりたいこと

main.hoge.hoge → routes/main.phpでルーティング
api.hoge.hoge → routes/api.phpでルーティング
test.hoge.hoge → routes/test.phpでルーティング

①テスト環境と本番環境でドメインを分けるためにconfig設定

configフォルダ内にmyapp.php作成
\Config::get('myapp.domain.main')でアクセスできる

config/myapp.php
<?php
return [
    'domain' => [
        'main' => 'main.hoge.hoge',
        'api' => 'api.hoge.hoge',
        'test' => 'test.hoge.hoge',
    ]
];

コンフィグファイルのキャッシュクリアをしないと反映されない。

$ php artisan config:cache

②ルーティング設定

/app/Providers/RouteServiceProvider.php
class RouteServiceProvider extends ServiceProvider
{

    public function map()
    {
        $this->mapApiRoutes();

        //$this->mapWebRoutes();
        $this->mapMainRoutes();
        $this->mapTestRoutes();
    }

    protected function mapMainRoutes()
    {
        Route::domain(\Config::get('myapp.domain.main'))
             ->middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/main.php'));
    }
    protected function mapTestRoutes()
    {
        Route::domain(\Config::get('myapp.domain.test'))
             ->middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/test.php'));
    }
    protected function mapApiRoutes()
    {
        Route::domain(\Config::get('api.domain.test'))
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }

routes/main.php,routes/test.phpを作ってルーティングさせる。

おわり。

CROS対応

サブドメインで分けるとaxiosからの通信がクロスドメインで引っかかるので設定
※バージョン指定しないとエラーになります。

$ composer require barryvdh/laravel-cors:^0.11

続きは下記参照
Laravelでクロスオリジン(CORS)に対応する為のメモです

※crosの設定を上に持ってこないと一部ヘッダが付与されない場合がある?

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'cros',
            'throttle:60,1',
            'bindings',

        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'cros' => \Barryvdh\Cors\HandleCors::class,
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];

セッション共有

.envにSESSION_DOMAIN設定すればサブドメイン間でセッション共有可能

'''.enb
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_DOMAIN='.hoge.hoge'
'''

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidスマホにApacheとPHPとMySQLをインストールする

UerLandを使ってAndroidスマホにApacheとPHPとMySQLをインストールします。

UserLandでUbuntuのsshをインストールするとポート2022番号でログインをすることができます。

ssh USERNAME@192.168.xxx.xxx -p2022 ;

ログインをしたらまずは最新にアップデート

sudo apt update ;
sudo apt upgrade -y ;
sudo apt install -y nano ;

Apache2 PHP MySQLをインストール

sudo apt install apache2 php php-mysql mariadb-server ;

Apacheの開放ポートを80から8080へ変更

emacs /etc/apache2/ports.conf ;

下記修正

Listen 80
↓
Listen 8080

サービスとして起動(systemctrlは使用できないため)

service apache2 start ;

他の端末からブラウザで

http://192.168.xxx.xxx:8080

を開く。

/var/www/html/内のファイルにアクセスができます。

PHPの動作確認

mv /var/www/html/index.html /var/www/html/index.html.back ;
nano /var/www/html/index.php ;

index.phpに下記を記述

<?php

phpinfo();

保存

再び

http://192.168.xxx.xxx:8080

を開くとphpの情報がズラリと出ます。

ワードプレスも行けそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Stoplight Studioで作成したswagger.yamlを並び替えて見やすくするプログラムを作った

Swagger.yamlを作るのにstoplightのツールはとても便利なのですが、
生成されるファイルは、ツールで登録した順に記述されるので、後でSwagger.yamlそのものを、見た時に並び順がバラバラで見づらくなります。
そこで、APIやComponentを昇順に並び替えて、中の項目も同じ順で並び替えるプログラムを作りました。

事前準備

yamlファイルの読み込み、書き出しにsymfony/yamlパッケージを使います。
composerを使ってインストールしてください。

例:

composer.json
{
    "require": {
        "symfony/yaml": "*"
    }
}

php composer install

使い方

以下のプログラムを、整形したいswaggerファイルを指定して実行するだけです。
指定したファイルの拡張子の手前に、_convertedとついたファイルが生成されます。

convert_swagger.php
<?php
require 'vendor/autoload.php';
use Symfony\Component\Yaml\Yaml;

// yamlファイルを読み込んでarray配列に変換する
$file_name = $argv[1];
$content = file_get_contents($file_name);
$yaml_array = Yaml::parse($content);

// APIの並びをURLの昇順にする
$paths = $yaml_array["paths"];
ksort($paths);
$yaml_array["paths"] = $paths;

// components schemasの並び順を昇順にする
$schemas = [];
if(array_key_exists("components", $yaml_array)){
    $schemas = $yaml_array["components"]["schemas"];
    ksort($schemas);
}

// APIの項目を並び替える
foreach($paths as $key1 => $value1){
    foreach($value1 as $key2 => $value2){
        $item = [];
        $item["operationId"] = $value2["operationId"];
        $item["tags"] = $value2["tags"];
        $item["summary"] = $value2["summary"];
        $item["description"] = $value2["description"];
        $item["parameters"] = $value2["parameters"];
        if(array_key_exists("requestBody", $value2)){
            $item["requestBody"] = $value2["requestBody"];
            unset($value2["requestBody"]);
        }
        $item["responses"] = $value2["responses"];
        $yaml_array["paths"][$key1][$key2] = $item;
    }
}

// components schemasの項目を並び替える
foreach($schemas as $key => $value){
    $item = [];
    $item["title"] = $value["title"];
    $item["description"] = $value["description"];
    $item["type"] = $value["type"];
    $item["properties"] = $value["properties"];
    $item["required"] = $value["required"];
    $yaml_array["components"]["schemas"][$key] = $item;
}

// 並び替えたデータをyaml形式に変換し、ファイルに出力する
$file_name = preg_replace("/\.yaml$/", "_converted.yaml", $file_name);
file_put_contents($file_name, Yaml::dump($yaml_array,99,2));
?>

実行例
php convert_swagger.php sample.v1.yaml
sample.v1_converted.yamlが生成されます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPUnitのバージョンアップで使えなくなったgetMock()の置き換え法(草刈り)

環境

  • php 5.6.21
  • phpUnit 5.7.27
  • Xdebug 2.4.0

前置き

たまたまPHPUnit5.7.27が入っている環境の触れる機会がありました。
この環境でテストを実行すると以下のように大量の警告が出てきて「何事だ!?」となったのが始まりです。
スクリーンショット 2020-09-02 16.40.51.png
↑大量の警告が出て大草原になっています
この警告を消して(草刈りして)いきたいと思います。

警告の内容

警告の内容は以下の通りです。

PHPUnit_Framework_TestCase::getMock() is deprecated, use PHPUnit_Framework_TestCase::createMock() or PHPUnit_Framework_TestCase::getMockBuilder() instead

getMockは廃止予定だからcreateMockgetMockBuilderに置き換えてねという内容です。

getMock()の仕様

舞台裏では、getMock() メソッドが使われたときに PHPUnit が自動的に、求める振る舞いを実装した新たな PHP のクラスを生成しています。 生成されるテストダブルクラスの設定は、 getMock() メソッドのオプションの引数を使って行います。
デフォルトでは、指定したクラスのすべてのメソッドが単に NULL を返すだけのテストダブルとなります。返り値を変更するには、たとえば will($this->returnValue()) を使います。
オプションの第二パラメータを指定すると、その配列の中に含まれる名前のメソッドだけがテストダブルに置き換えらて、その他のメソッドはそのままとなります。パラメータに NULL を渡すと、どのメソッドも置き換えません。
オプションの第三パラメータには、元クラスのコンストラクタに渡すパラメータの配列を渡します (デフォルトでは、コンストラクタはダミー実装に置き換えられません)。
オプションの第四パラメータを使うと、生成されるテストダブルクラスのクラス名を指定することができます。
オプションの第五パラメータを使うと、元クラスのコンストラクタを呼び出さないようにすることができます。
オプションの第六パラメータを使うと、元クラスの clone コンストラクタを呼び出さないようにすることができます。
オプションの第七パラメータを使うと、テストダブルクラスの生成時に __autoload() を無効にすることができます。
https://phpunit.de/manual/3.7/ja/test-doubles.html

これを置き換えていきます!

getMockBuilderで置き換えていく!

仕様を読んで置き換えていくと以下のようになります。

getMock(hoge1,hoge2,hoge3,hoge4,hoge5,hoge6,hoge7);
// 置き換え後
getMockBuilder(hoge1)
  ->setMethods(hoge2)                 // 置き換えたいメソッドを指定
  ->setConstructorArgs(hoge3)         // コンストラクタに渡す引数を指定
  ->setMockClassName(hoge4)           // 生成されるテストダブルクラスのクラス名を指定
  ->disableOriginalConstructor(hoge5) // 元クラスのクローンコンストラクタを無効にする
  ->disableOriginalClone(hoge6)       // 元クラスのクローンコンストラクタを無効にする
  ->disableAutoload(hoge7)            // __autoload()を無効にする
  ->getMock();                       // モックの取得

今回は置き換えの該当箇所がとても多いので、getMockをオーバーライドする形で置き換えます。
適当な場所に以下のようなファイルを作成します。

tests/TestAdapter.php
<?php
class TestAdapter extends PHPUnit_Framework_TestCase
{
    public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)
    {
        // モックの生成
        $mockObject = $this->getMockBuilder($originalClassName);
        // 置き換えたいメソッドを指定。
        if (!empty($methods)) {
            $mockObject = $mockObject->setMethods($methods);
        }
        // コンストラクタに渡す引数を指定
        if (!empty($arguments)) {
            $mockObject = $mockObject->setConstructorArgs($arguments);
        }
        if ($mockClassName != '') {
            $mockObject = $mockObject->setMockClassName($mockClassName);
        }
        // 元クラスのクローンコンストラクタを無効にする
        if (!$callOriginalConstructor) {
            $mockObject = $mockObject->disableOriginalConstructor();
        }
        // 元クラスのクローンコンストラクタを無効にする
        if (!$callOriginalClone) {
            $mockObject = $mockObject->disableOriginalClone();
        }
        // __autoload()を無効にする
        if (!$callAutoload) {
            $mockObject = $mockObject->disableAutoload();
        }
        // 生成
        $mockObject = $mockObject->getMock();
        return $mockObject;
    }
}

これをinit.phpで呼び出します。

tests/init.php
require_once dirname(__FILE__) . '/TestAdapter.php';// 適当な場所に追記してください。

あとは各テストでPHPUnit_Framework_TestCaseを呼び出していた箇所をTestAdapterに変えていけばOKです!

- class hogetest extends PHPUnit_Framework_TestCase
+ class hogetest extends TestAdapter

置き換え後のテスト結果

スクリーンショット 2020-09-11 12.17.5.png

きれいになりました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでWindows向けのCSVデータをLinuxサーバにて生成する。

手間だった為、関数を用意しておく。

生成するCSVデータ

  • 文字コード = Shift-JIS
  • 改行コード = ¥r¥n
    • ¥r = キャリッジ・リターン(carriage return、復帰)
    • ¥n = ライン・フィード(line feed、改行)
  • 項目を囲む文字 = 「"」(ダブルコーテーション)

関数

/**
 * 二次元配列からWindows向けのCSVデータを生成する。
 *
 * @param array $records
 * @param string $delimiter
 * @param string $enclosure
 * @param string $enclosure_escape
 * @param string $line_break
 * @param string $from_encoding
 * @param string $to_encoding
 * @param int $maxmemory
 * @return string
 */
function createWinCSV(
    $records,
    $delimiter=',', $enclosure='"', $enclosure_escape='""', $line_break="\r\n", 
    $from_encoding='UTF-8', $to_encoding='SJIS-win', 
    $maxmemory=5242880
        ){
    $fp = fopen("php://temp/maxmemory:" . $maxmemory, 'r+b');
    $delimiter = $enclosure . $delimiter . $enclosure;
    foreach ($records as $record) {
        $line = $enclosure
                . implode($delimiter, array_map(
                    function($val) use($enclosure, $enclosure_escape) 
                        {return str_replace($enclosure, $enclosure_escape, $val);}, 
                    $record))
                . $enclosure
                . $line_break;
        fwrite($fp, $line);
    }
    rewind($fp);
    $tmp = stream_get_contents($fp);
    fclose($fp);
    return mb_convert_encoding($tmp, $to_encoding, $from_encoding);
}

使用例

$records = [
    ['id', 'name', 'birthday', 'comment'],
    ['0001', '佐藤', '1980/01/02', 'Thanks!'],
    ['0002', '鈴木', '1990/03/04', 'Hello.'],
    ['0003', '田中', '2000/05/06', 'I say "Hello".']
];

print(createWinCSV($records));
# "id","name","birthday","comment"
# "0001","����","1980/01/02","Thanks!"
# "0002","���","1990/03/04","Hello."
# "0003","�c��","2000/05/06","I say ""Hello""."

参考

生成したCSVデータをCSVファイルとしてダウンロードする箇所は、次のページを参考にさせて頂いた。
【PHP】【CakePHP】CSVダウンロード - Qiita

備考

本記事はブログ「雑用エンジニアの技術ノート」からの移行記事です。先のブログは削除予定です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Web系の定番ソフトウェアのバージョンを確認する。

次のコマンドを発行する。

CentOS

cat /etc/redhat-release

Apache

httpd -v

Java

java -version

Python

python --version
python3 --version

PHP

php -v

WordPress

cat wordpress/wp-includes/version.php
# $wp_versionの値を確認する。

MySQL

-- MySQL接続後に次のSQL文を発行する。
select version();

Oracle Database

-- OracleDB接続後に次のSQL文を発行する。
select * from v$version;

備考

本記事はブログ「雑用エンジニアの技術ノート」からの移行記事です。先のブログは削除予定です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8を試したら即効でエラー「Target class [〇〇〇Controller] does not exist.」が表示された

はじめに

最近になってLaravel8がリリースされたと聞いて、とりあえず動かしてみようと思ったら開始早々にエラーが発生しました。
laravel8エラー.png
web.phpPostControllerを呼び出す処理を書いたのに上記エラーが発生。Laravel6ではちゃんと呼び出せたのに:joy:
そのときのweb.phpはこちら。

web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', 'PostController@index');

原因調査したら、どうやらLaravel8の変更点の一つであるRouteServiceProvider.phpファイルからデフォルトの名前空間を削除」したのが大きく関係していると判明!Laravel7.xのプロジェクトからLaravel8.xに移行した場合は、この変更点の影響は受けない。Laravel8で作成した新規プロジェクトは変更点を考慮する必要があるそうです。

RouteServiceProvider.phpの変化

Laravel7までは、RouteServiceProvider.phpには次のコードが含まれていました。

RouteServiceProvider.php
protected $namespace = 'App\Http\Controllers';

Route::middleware('web')
      ->namespace($this->namespace)
      ->group(base_path('routes/web.php'));

webミドルウェアとApp\Http\Controllersの名前空間を使用して、routes/web.phpにルートをロードするよう指示しています。

web.php
// Laravelは、App\Http\Controllers\PostControllerを検索します
Route::get('/', 'PostController@index');

Laravel8では、$namespace変数が削除され、Route宣言が次のように変更されました。

RouteServiceProvider.php
Route::middleware('web')
      ->group(base_path('routes/web.php'));

App\Http\Controllersの名前空間を使用していないです。

web.php
// Laravelは、App\Http\Controllers内でコントローラーを検索しません
Route::get('/', 'PostController@index');

このあと、どう修正すればよいのか解決方法を3つご紹介します。

解決方法

  • RouteServiceProvider.phpで名前空間を手動で追加する
  • web.phpで完全な名前空間を使用する
  • web.phpでアクション構文を使用する

RouteServiceProvider.phpで名前空間を手動で追加する

Laravel7.x以前と同じ方法です。

RouteServiceProvider.php
protected $namespace = 'App\Http\Controllers'; //追加
public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::middleware('web')
                ->namespace($this->namespace) //追加
                ->group(base_path('routes/web.php'));

            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace) //追加
                ->group(base_path('routes/api.php'));
        });
    }

web.phpで完全な名前空間を使用する

コントローラーの名前の前に名前空間を追加します。
例として、app/Http/Controllersフォルダ内のPostControllerを呼び出したいときは、次の通り記載します。

web.php
Route::get('/', 'App\Http\Controllers\PostController@index');

web.phpでアクション構文を使用する

web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
Route::get('/', [PostController::class, 'index']);

配列で使用したいクラスとメソッドを指定します。
具体的に配列内の値は
PostsController :: class -> App \ Http \ Controllers \ PostsControllerを返している
index -> PostController.phpのindexメソッドを呼び出している

終わりに

Laravel開発で初めて遭遇したエラーでしたので勉強になりました。
ただ、Laravel8の新機能・変更点を理解してから使用してみようと思いました。理解せず使用すると痛い目に遭いますね:joy:

参考

How to fix ‘Target class does not exist’ in Laravel 8

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでneo4jを使う④マイグレーション

事前準備

・app/database/labelsフォルダの作成

$ php artisan neo4j:make:migration create_race_label

$ php artisan neo4j:migrate

コマンド  説明
$label->unique('email') プロパティに一意の制約を追加する
$label->dropUnique('email') プロパティから一意の制約を削除する
$label->index('uuid') プロパティにインデックスを追加する
$label->dropIndex('uuid') プロパティからインデックスを削除しています

コマンド 説明

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む