20210726のPHPに関する記事は12件です。

プログラミング初心者が書いたバリデーションを日本語に変える方法

はじめに Laravelにてフォームを作成した際に、必ずといっていいほどバリデーションを実装すると思います。 最初の段階では英語の状態になっていて、個人的には日本語がいいなぁと思い色々と調べてみた結果 割と簡単に日本語のエラーメッセージに直せることが分かったのでメモとして残します。 日本語バリデーションの実装 現在Laravel青本を読みながらフォームを作っておりまして、現在の段階はこんな感じです。 なんだか英語ばっかりで嫌になっちゃいますね。。。 というわけで日本語に早速変えていきます。 まずは/config/app.phpの中を変えていきます。 app.php |-------------------------------------------------------------------------- | Application Timezone |-------------------------------------------------------------------------- | | Here you may specify the default timezone for your application, which | will be used by the PHP date and date-time functions. We have gone | ahead and set this to a sensible default for you out of the box. | */ 'timezone' => 'UTC', /* |-------------------------------------------------------------------------- | Application Locale Configuration |-------------------------------------------------------------------------- | | The application locale determines the default locale that will be used | by the translation service provider. You are free to set this value | to any of the locales which will be supported by the application. | */ 'locale' => 'en', ここの'locale' => 'en',を'locale' => 'ja',に変更します。 次にlangフォルダの中にenという初期から入っているフォルダがあるので それを丸々コピーして同様にlangフォルダの直下に置いておきます。 そしてjaの中のvalidation.phpのコードを全て下記のものに変えます。 validation.php <?php return [ /* |-------------------------------------------------------------------------- | Validation Language Lines |-------------------------------------------------------------------------- | | The following language lines contain the default error messages used by | the validator class. Some of these rules have multiple versions such | as the size rules. Feel free to tweak each of these messages here. | */ 'accepted' => ':attributeを承認してください。', 'active_url' => ':attributeは、有効なURLではありません。', 'after' => ':attributeには、:dateより後の日付を指定してください。', 'after_or_equal' => ':attributeには、:date以降の日付を指定してください。', 'alpha' => ':attributeには、アルファベッドのみ使用できます。', 'alpha_dash' => ":attributeには、英数字('A-Z','a-z','0-9')とハイフンと下線('-','_')が使用できます。", 'alpha_num' => ":attributeには、英数字('A-Z','a-z','0-9')が使用できます。", 'array' => ':attributeには、配列を指定してください。', 'before' => ':attributeには、:dateより前の日付を指定してください。', 'before_or_equal' => ':attributeには、:date以前の日付を指定してください。', 'between' => [ 'numeric' => ':attributeには、:minから、:maxまでの数字を指定してください。', 'file' => ':attributeには、:min KBから:max KBまでのサイズのファイルを指定してください。', 'string' => ':attributeは、:min文字から:max文字にしてください。', 'array' => ':attributeの項目は、:min個から:max個にしてください。', ], 'boolean' => ":attributeには、'true'か'false'を指定してください。", 'confirmed' => ':attributeと:attribute確認が一致しません。', 'date' => ':attributeは、正しい日付ではありません。', 'date_equals' => ':attributeは:dateに等しい日付でなければなりません。', 'date_format' => ":attributeの形式は、':format'と合いません。", 'different' => ':attributeと:otherには、異なるものを指定してください。', 'digits' => ':attributeは、:digits桁にしてください。', 'digits_between' => ':attributeは、:min桁から:max桁にしてください。', 'dimensions' => ':attributeの画像サイズが無効です', 'distinct' => ':attributeの値が重複しています。', 'email' => ':attributeは、有効なメールアドレス形式で指定してください。', 'ends_with' => 'The :attribute must end with one of the following: :values', 'exists' => '選択された:attributeは、有効ではありません。', 'file' => ':attributeはファイルでなければいけません。', 'filled' => ':attributeは必須です。', 'gt' => [ 'numeric' => ':attributeは、:valueより大きくなければなりません。', 'file' => ':attributeは、:value KBより大きくなければなりません。', 'string' => ':attributeは、:value文字より大きくなければなりません。', 'array' => ':attributeの項目数は、:value個より大きくなければなりません。', ], 'gte' => [ 'numeric' => ':attributeは、:value以上でなければなりません。', 'file' => ':attributeは、:value KB以上でなければなりません。', 'string' => ':attributeは、:value文字以上でなければなりません。', 'array' => ':attributeの項目数は、:value個以上でなければなりません。', ], 'image' => ':attributeには、画像を指定してください。', 'in' => '選択された:attributeは、有効ではありません。', 'in_array' => ':attributeが:otherに存在しません。', 'integer' => ':attributeには、整数を指定してください。', 'ip' => ':attributeには、有効なIPアドレスを指定してください。', 'ipv4' => ':attributeはIPv4アドレスを指定してください。', 'ipv6' => ':attributeはIPv6アドレスを指定してください。', 'json' => ':attributeには、有効なJSON文字列を指定してください。', 'lt' => [ 'numeric' => ':attributeは、:valueより小さくなければなりません。', 'file' => ':attributeは、:value KBより小さくなければなりません。', 'string' => ':attributeは、:value文字より小さくなければなりません。', 'array' => ':attributeの項目数は、:value個より小さくなければなりません。', ], 'lte' => [ 'numeric' => ':attributeは、:value以下でなければなりません。', 'file' => ':attributeは、:value KB以下でなければなりません。', 'string' => ':attributeは、:value文字以下でなければなりません。', 'array' => ':attributeの項目数は、:value個以下でなければなりません。', ], 'max' => [ 'numeric' => ':attributeには、:max以下の数字を指定してください。', 'file' => ':attributeには、:max KB以下のファイルを指定してください。', 'string' => ':attributeは、:max文字以下にしてください。', 'array' => ':attributeの項目は、:max個以下にしてください。', ], 'mimes' => ':attributeには、:valuesタイプのファイルを指定してください。', 'mimetypes' => ':attributeには、:valuesタイプのファイルを指定してください。', 'min' => [ 'numeric' => ':attributeには、:min以上の数字を指定してください。', 'file' => ':attributeには、:min KB以上のファイルを指定してください。', 'string' => ':attributeは、:min文字以上にしてください。', 'array' => ':attributeの項目は、:min個以上にしてください。', ], 'not_in' => '選択された:attributeは、有効ではありません。', 'not_regex' => ':attributeの形式が無効です。', 'numeric' => ':attributeには、数字を指定してください。', 'password' => ':attributeが間違っています', 'present' => ':attributeが存在している必要があります。', 'regex' => ':attributeには、有効な正規表現を指定してください。', 'required' => ':attributeは、必ず指定してください。', 'required_if' => ':otherが:valueの場合、:attributeを指定してください。', 'required_unless' => ':otherが:values以外の場合、:attributeを指定してください。', 'required_with' => ':valuesが指定されている場合、:attributeも指定してください。', 'required_with_all' => ':valuesが全て指定されている場合、:attributeも指定してください。', 'required_without' => ':valuesが指定されていない場合、:attributeを指定してください。', 'required_without_all' => ':valuesが全て指定されていない場合、:attributeを指定してください。', 'same' => ':attributeと:otherが一致しません。', 'size' => [ 'numeric' => ':attributeには、:sizeを指定してください。', 'file' => ':attributeには、:size KBのファイルを指定してください。', 'string' => ':attributeは、:size文字にしてください。', 'array' => ':attributeの項目は、:size個にしてください。', ], 'starts_with' => ':attributeは、次のいずれかで始まる必要があります。:values', 'string' => ':attributeには、文字を指定してください。', 'timezone' => ':attributeには、有効なタイムゾーンを指定してください。', 'unique' => '指定の:attributeは既に使用されています。', 'uploaded' => ':attributeのアップロードに失敗しました。', 'url' => ':attributeは、有効なURL形式で指定してください。', 'uuid' => ':attributeは、有効なUUIDでなければなりません。', /* |-------------------------------------------------------------------------- | Custom Validation Language Lines |-------------------------------------------------------------------------- | | Here you may specify custom validation messages for attributes using the | convention "attribute.rule" to name the lines. This makes it quick to | specify a specific custom language line for a given attribute rule. | */ 'custom' => [ 'attribute-name' => [ 'rule-name' => 'custom-message', ], ], /* |-------------------------------------------------------------------------- | Custom Validation Attributes |-------------------------------------------------------------------------- | | The following language lines are used to swap our attribute placeholder | with something more reader friendly such as "E-Mail Address" instead | of "email". This simply helps us make our message more expressive. | */ 'attributes' => [], ]; これに関しては丸々コピペで大丈夫です。 そして再びフォームの方で実行してみると。。。。 という風に日本語に変わりましたね! これにてバリデーションの変更は完了です。 何か誤っている点などがありましたらご指摘お願いします! 参考資料 https://qiita.com/ama_keshi/items/27292949d41fdd8bd930 https://s-yqual.com/blog/914
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Cloud9 でAmazon Linux 2でLaravelの設定

Amazon Linux2 ではEPELが使えない EPELが何かは知りません。調べたら追記します! とりあえず仕様している教材に amazon linuxを使ってください。linux 2ではEPELが使えないので。 みたいに書いてあったけど、 AWSのCloud9でワークスペースを作成する際に、 Amazon Linux 2 (recommended) と合ったので、せっかくなら推奨版が使いたいなということで、環境を少しアレンジ。 まああとは自分で調べてEPELが使えればいいだろうという考え。というか推奨版なんだから当然使えるっしょ!の乗りです。 手動でEPELを有効にする この通りにやれば大丈夫っぽい。 sudo amazon-linux-extras install epel -y phpのバージョン確認 $ php -v PHP 7.2.24 (cli) (built: Oct 31 2019 18:27:08) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies 2021/07/26の段階では7.2.24が標準でインストールされるらしい。 amazon linuxの方だと5系になるのかな?? composeのインストール $ curl -sS https://getcomposer.org/installer | php All settings correct for using Composer Downloading... Composer (version 2.1.5) successfully installed to: /home/ec2-user/environment/composer.phar Use it: php composer.phar # このままだとcomposerのパスが通ってない? $ composer bash: composer: command not found # composer.pharを移動してパスを通す $ sudo mv composer.phar /usr/local/bin/composer # 再確認 $ composer ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.1.5 2021-07-23 10:35:47 Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --profile Display timing and memory usage information --no-plugins Whether to disable plugins. -d, --working-dir=WORKING-DIR If specified, use the given directory as working directory. --no-cache Prevent use of the cache -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: about Shows a short information about Composer. archive Creates an archive of this composer package. browse Opens the package's repository URL or homepage in your browser. cc Clears composer's internal package cache. check-platform-reqs Check that platform requirements are satisfied. clear-cache Clears composer's internal package cache. clearcache Clears composer's internal package cache. config Sets config options. create-project Creates new project from a package into given directory. depends Shows which packages cause the given package to be installed. diagnose Diagnoses the system to identify common errors. dump-autoload Dumps the autoloader. dumpautoload Dumps the autoloader. exec Executes a vendored binary/script. fund Discover how to help fund the maintenance of your dependencies. global Allows running commands in the global composer dir ($COMPOSER_HOME). help Displays help for a command home Opens the package's repository URL or homepage in your browser. i Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. info Shows information about packages. init Creates a basic composer.json file in current directory. install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. licenses Shows information about licenses of dependencies. list Lists commands outdated Shows a list of installed packages that have updates available, including their latest version. prohibits Shows which packages prevent the given package from being installed. reinstall Uninstalls and reinstalls the given package names remove Removes a package from the require or require-dev. require Adds required packages to your composer.json and installs them. run Runs the scripts defined in composer.json. run-script Runs the scripts defined in composer.json. search Searches for packages. self-update Updates composer.phar to the latest version. selfupdate Updates composer.phar to the latest version. show Shows information about packages. status Shows a list of locally modified packages. suggests Shows package suggestions. u Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. update Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. upgrade Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. validate Validates a composer.json and composer.lock. why Shows which packages cause the given package to be installed. why-not Shows which packages prevent the given package from being installed. いけた! Laravelプロジェクトを作成 $ composer create-project laravel/laravel ./laravel-quest "5.5.*" --prefer-distCreating a "laravel/laravel" project at "./laravel-quest" Installing laravel/laravel (v5.5.28) - Downloading laravel/laravel (v5.5.28) - Installing laravel/laravel (v5.5.28): Extracting archive Created project in /home/ec2-user/environment/./laravel-quest > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies Your requirements could not be resolved to an installable set of packages. Problem 1 - laravel/framework[v5.5.0, ..., v5.5.50] require ext-mbstring * -> it is missing from your system. Install or enable PHP's mbstring extension. - Root composer.json requires laravel/framework 5.5.* -> satisfiable by laravel/framework[v5.5.0, ..., v5.5.50]. - なんか問題が発生している。。。 laravel/framework[v5.5.0, ..., v5.5.50] require ext-mbstring * -> it is missing from your system. Install or enable PHP's mbstring extension. extensionが無い、もしくは有効になっていないということですね。 ちなみにこの状態でもLaravelプロジェクトは作成されています。 しかし起動しようとするとできません。 $ php artisan serve --port=80 PHP Warning: require(/home/ec2-user/environment/laravel-quest/vendor/autoload.php): failed to open stream: No such file or directory in /home/ec2-user/environment/laravel-quest/artisan on line 18 PHP Fatal error: require(): Failed opening required '/home/ec2-user/environment/laravel-quest/vendor/autoload.php' (include_path='.:/usr/share/pear:/usr/share/php') in /home/ec2-user/environment/laravel-quest/artisan on line 18 autoload.phpがないって言われてるんですね。 よくわかりませんが、serverが入ってないから起動できないということだと思います。 なので手動で入れる。 WebサーバApacheはamazon-linux-extrasにないらしい ここのとおりにやれば大丈夫でした。 # aparchのインストール $ sudo yum install -y httpd Loaded plugins: extras_suggestions, langpacks, priorities, update-motd amzn2-core | 3.7 kB 00:00:00 242 packages excluded due to repository priority protections Package httpd-2.4.48-2.amzn2.x86_64 already installed and latest version Nothing to do # 入ってるやん! # サーバの確認 $ httpd -v Server version: Apache/2.4.48 () // 入ってます! Server built: Jun 25 2021 18:53:37 # php関連のインストール $ sudo yum install php php-mbstring php-pdo php-gd php-xml Loaded plugins: extras_suggestions, langpacks, priorities, update-motd 242 packages excluded due to repository priority protections Package php-pdo-7.2.24-1.amzn2.0.1.x86_64 already installed and latest version Package php-xml-7.2.24-1.amzn2.0.1.x86_64 already installed and latest version Resolving Dependencies --> Running transaction check ---> Package php.x86_64 0:7.2.24-1.amzn2.0.1 will be installed ---> Package php-gd.x86_64 0:7.2.24-1.amzn2.0.1 will be installed --> Processing Dependency: libXpm.so.4()(64bit) for package: php-gd-7.2.24-1.amzn2.0.1.x86_64 --> Processing Dependency: libX11.so.6()(64bit) for package: php-gd-7.2.24-1.amzn2.0.1.x86_64 ---> Package php-mbstring.x86_64 0:7.2.24-1.amzn2.0.1 will be installed --> Processing Dependency: libonig.so.2()(64bit) for package: php-mbstring-7.2.24-1.amzn2.0.1.x86_64 --> Running transaction check ---> Package libX11.x86_64 0:1.6.7-3.amzn2.0.2 will be installed --> Processing Dependency: libX11-common >= 1.6.7-3.amzn2.0.2 for package: libX11-1.6.7-3.amzn2.0.2.x86_64 --> Processing Dependency: libxcb.so.1()(64bit) for package: libX11-1.6.7-3.amzn2.0.2.x86_64 ---> Package libXpm.x86_64 0:3.5.12-1.amzn2.0.2 will be installed ---> Package oniguruma.x86_64 0:5.9.6-1.amzn2.0.4 will be installed --> Running transaction check ---> Package libX11-common.noarch 0:1.6.7-3.amzn2.0.2 will be installed ---> Package libxcb.x86_64 0:1.12-1.amzn2.0.2 will be installed --> Processing Dependency: libXau.so.6()(64bit) for package: libxcb-1.12-1.amzn2.0.2.x86_64 --> Running transaction check ---> Package libXau.x86_64 0:1.0.8-2.1.amzn2.0.2 will be installed --> Finished Dependency Resolution Dependencies Resolved ====================================================================================================== Package Arch Version Repository Size ====================================================================================================== Installing: php x86_64 7.2.24-1.amzn2.0.1 amzn2extra-lamp-mariadb10.2-php7.2 2.9 M php-gd x86_64 7.2.24-1.amzn2.0.1 amzn2extra-lamp-mariadb10.2-php7.2 179 k php-mbstring x86_64 7.2.24-1.amzn2.0.1 amzn2extra-lamp-mariadb10.2-php7.2 501 k Installing for dependencies: libX11 x86_64 1.6.7-3.amzn2.0.2 amzn2-core 606 k libX11-common noarch 1.6.7-3.amzn2.0.2 amzn2-core 165 k libXau x86_64 1.0.8-2.1.amzn2.0.2 amzn2-core 29 k libXpm x86_64 3.5.12-1.amzn2.0.2 amzn2-core 57 k libxcb x86_64 1.12-1.amzn2.0.2 amzn2-core 216 k oniguruma x86_64 5.9.6-1.amzn2.0.4 amzn2-core 127 k Transaction Summary ====================================================================================================== Install 3 Packages (+6 Dependent packages) Total download size: 4.7 M Installed size: 16 M Is this ok [y/d/N]: y Downloading packages: (1/9): libX11-common-1.6.7-3.amzn2.0.2.noarch.rpm | 165 kB 00:00:00 (2/9): libXau-1.0.8-2.1.amzn2.0.2.x86_64.rpm | 29 kB 00:00:00 (3/9): libXpm-3.5.12-1.amzn2.0.2.x86_64.rpm | 57 kB 00:00:00 (4/9): libX11-1.6.7-3.amzn2.0.2.x86_64.rpm | 606 kB 00:00:00 (5/9): oniguruma-5.9.6-1.amzn2.0.4.x86_64.rpm | 127 kB 00:00:00 (6/9): libxcb-1.12-1.amzn2.0.2.x86_64.rpm | 216 kB 00:00:00 (7/9): php-gd-7.2.24-1.amzn2.0.1.x86_64.rpm | 179 kB 00:00:00 (8/9): php-7.2.24-1.amzn2.0.1.x86_64.rpm | 2.9 MB 00:00:00 (9/9): php-mbstring-7.2.24-1.amzn2.0.1.x86_64.rpm | 501 kB 00:00:00 ------------------------------------------------------------------------------------------------------ Total 10 MB/s | 4.7 MB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : oniguruma-5.9.6-1.amzn2.0.4.x86_64 1/9 Installing : libXau-1.0.8-2.1.amzn2.0.2.x86_64 2/9 Installing : libxcb-1.12-1.amzn2.0.2.x86_64 3/9 Installing : libX11-common-1.6.7-3.amzn2.0.2.noarch 4/9 Installing : libX11-1.6.7-3.amzn2.0.2.x86_64 5/9 Installing : libXpm-3.5.12-1.amzn2.0.2.x86_64 6/9 Installing : php-gd-7.2.24-1.amzn2.0.1.x86_64 7/9 Installing : php-mbstring-7.2.24-1.amzn2.0.1.x86_64 8/9 Installing : php-7.2.24-1.amzn2.0.1.x86_64 9/9 Verifying : libX11-1.6.7-3.amzn2.0.2.x86_64 1/9 Verifying : php-mbstring-7.2.24-1.amzn2.0.1.x86_64 2/9 Verifying : libxcb-1.12-1.amzn2.0.2.x86_64 3/9 Verifying : libX11-common-1.6.7-3.amzn2.0.2.noarch 4/9 Verifying : php-7.2.24-1.amzn2.0.1.x86_64 5/9 Verifying : libXpm-3.5.12-1.amzn2.0.2.x86_64 6/9 Verifying : libXau-1.0.8-2.1.amzn2.0.2.x86_64 7/9 Verifying : php-gd-7.2.24-1.amzn2.0.1.x86_64 8/9 Verifying : oniguruma-5.9.6-1.amzn2.0.4.x86_64 9/9 Installed: php.x86_64 0:7.2.24-1.amzn2.0.1 php-gd.x86_64 0:7.2.24-1.amzn2.0.1 php-mbstring.x86_64 0:7.2.24-1.amzn2.0.1 Dependency Installed: libX11.x86_64 0:1.6.7-3.amzn2.0.2 libX11-common.noarch 0:1.6.7-3.amzn2.0.2 libXau.x86_64 0:1.0.8-2.1.amzn2.0.2 libXpm.x86_64 0:3.5.12-1.amzn2.0.2 libxcb.x86_64 0:1.12-1.amzn2.0.2 oniguruma.x86_64 0:5.9.6-1.amzn2.0.4 Complete! # 以下を実行してautoload.phpを作成する $ composer install # 以下でサーバが起動できます。。。が!ブラウザは開けません! $ php artisan serve --port=8080 No application encryption key has been specified.が発生してブラウザが開けない https://qiita.com/ponsuke0531/items/197c76fcb9300d7c5f36 ここのとおりにやったら大丈夫です。 $ cat .env | grep ^APP_ APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost # 以下を実行するとAPP_KEYに自動的に追記される $ php artisan key:generate $ cat .env | grep ^APP_ APP_NAME=Laravel APP_ENV=local APP_KEY=base64:dvObQrZ9xEdYJaaaaaaaaaa/uFU3ADAsKtxxxxxxxxx= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost # 設定ファイルのキャッシュを再作成する $ php artisan config:cache Configuration cache cleared! Configuration cached successfully! これでブラウザが起動できるはず! やっとスタートラインです!頑張りましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel6.x系cloud9使っての開発

SQLSTATE[HY000] [1049] Unknown database 'laravel'と出た時の解消法 php artisan cache:clear を実行し、サーバーを再起動したら治りました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET(VB.NET C#) の配列変数 ← JSON → PHP

PHP (Webサーバ)と .NET アプリでデータをやりとりしたい場合、バイナリのやりとりはできません。ですのでデータをテキストに変換し HTTP POST するやりかたになります。 何に変換するかは任意ですが、古くは XML、こんにちは JSON に変換されることが多いようです。 PHPの場合、少なくともPHP7 では標準で JSON エンコード・デコード関数があります。 .NET の場合、いろいろとあるようですが、「JSON.NET」というのがお勧めです。( VS2013, VS2019 で動確) 早速ですが、PHP ← (JSON) → .NET  なサンプルを示します。 $dat1=json_decode($_POST['POSTdat1']); //1.配列全体を返す場合 echo json_encode($dat1, JSON_UNESCAPED_UNICODE); //2.配列の要素を出力する場合 //echo $dat1[0]; '配列をPOST変数として送りたい場合の処理について 'HTTPのWebリクエストでは電文(str)しか送れません。なので配列を送りたいときはJSONとして文字にして, 'そのJSON文字を受け取ったPHP側はデコードすることで配列化するのです。1次元のみならず多次元配列も可能です Dim dat1 As Object = {123, "aaa"} Dim Dat1str As String = Newtonsoft.Json.JsonConvert.SerializeObject(dat1) '送りたい配列をJSON文字列化 Dim url As String = "http://localhost/display.php" Dim wc As New System.Net.WebClient Dim ps As New System.Collections.Specialized.NameValueCollection ps.Add("POSTdat1", Dat1str) '配列などオブジェクトをJSON文字列化して送る ' wc.Credentials = New NetworkCredential("user", "pass") 'BASIC認証がかけられている場合 Dim resData As Byte() = wc.UploadValues(url, ps) wc.Dispose() Dim echoback As String = System.Text.Encoding.UTF8.GetString(resData) '実際にはこのあとHTML文章のうちデータ相当を抜き出す処理が必要(PHPはHTML文章のため意図しない文字が含まれているため) '1.PHPからJSONが出力される場合の処理 Dim resdat1 As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(echoback) '受け取ったJSON文字列をオブジェクトにデコード Console.WriteLine(resdat1(0)) 'エコーバック表示(配列0) '2.PHPから配列要素0が出力される場合 ' Console.WriteLine(echoback) 'ただの文字列なのでデータではない。だから唯の要素がPHPから帰ってくる場合もデシリアライズすること Console.WriteLine(resdat1(0)) 'データ '注意:PHPが echo するときHTML文章全体がこちらに帰ってきます。DeserializeObjectを実行するとき 'スペースやタブ、改行ぐらいなら除いてくれますが、その他のゴミのデータが入っていたら正常になりません。 'なので前にも述べたとおり、実用的には帰ってきた文字列から意図する文字列を抽出してからデシリアライズするべきでしょう。 PHP から VB へ受け取る場合、 PHPはページ全体を出力しますので、余計なゴミが含まれていたら正しくデシリアライズできません。 なのでこんな感じで、 echo 'PREJSON'.json_encode($dat1, JSON_UNESCAPED_UNICODE).'POSTJSON'; エンコードする部分を、任意の文字(例では PRESON, POSTJSON) で囲ってあげて、 VB側では正規表現で抽出してあげるとよいでしょう。 Dim echoback As String = System.Text.Encoding.UTF8.GetString(resData) echoback= Regex.Matches(echoback, "PREJSON(.*?)POSTJSON")(0).Groups(1)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

かな検索/漢字検索選択できる候補出現PHPクラス

漢字・かなの部分一致で住所検索ができる PHPクラス、Ajax Javascript です。クラス化しているのでカスタマイズが容易です。外部の API を使うよりPHPクラスを自前で内部で持っておいたほうが便利ではないでしょうか。 また住所検索のみならず他にも流用できます。私は商品の部分一致検索にも利用しています。要するに、 このような機能を実装しました。ローカルな住所検索の場合、「ほた」と打つだけで、東区保田窪、中央区保田窪が候補に出てくるわけです。 もしネットショップのように全国区であれば、クラスを使うときに設定するインスタンスで引くフィールドを変更すればどのようにも対応できるわけです。データは郵政が公開されているCSVを整理したものです。 これは DataSet にフリガナの列があるわけである。しかしフリガナの列がなく漢字検索のみにしたい場合などもあると思う。そういう用途にも使いまわせるようにクラスとして汎化した。 ・「漢字/かな」検索、「漢字」検索ができる ・データは JSON 形式、 SQL に対応。 ただし、SQL の場合、汎化のためテーブル全体を引いている。 (全体を引いているため PHP から JS へ連想配列として帰ってきデータから表示させたいフィールドを自由に選べる利点がある一方、サーバリソースのことも考える必要がある) ・重複データは削除 なお熊本県の市町村JSONデータについてはこちら(全国区のデータもご用意したかたったのですが、正規化(ページ最下部)の作業がめんどうなので熊本だけ用意) このデータは郵政株式会社の site からダウンロードできるCSVデータをもとに、「同じ町名でも郵便番号が異なる」いくつかの例外を正規化した 郵便番号(zip)、市区のかな、町名のかな、市区の漢字、町名の漢字からなる JSON データである。PHP であれば以下の decode関数でただちに使えるデータである。 json_decode(file_get_contents("JSON", false),true); 次に住所検索クラスを示します。 <?php /* ##################### クラスの利用例 ########################### */ if(isset($_POST['fnc']) && $_POST['fnc']=="getItem"){getItem($_POST["value"]);} if(isset($_POST['fnc']) && $_POST['fnc']=="getTown"){getTown($_POST["value"]);} function getItem($value){ $ins = new TableData(); $ins->IsSQL=true; //SQLを用いる $ins->UseKana=true; //かな検索を用いる $ins->UseKata=false; //かな検索フィールドがひらがなである $ins->Duplicate=false; //漢字検索フィールドの重複を省かない $ins->TableName="テーブル名"; $ins->searchValue=$value; $ins->searchField="漢字検索フィールド"; $ins->searchFieldKana="かな検索フィールド"; $ins->sendFilteredJSON(); } function getTown($value){ $ins = new TableData(); $ins->IsSQL=false; //JSONを用いる $ins->UseKana=true; //かな検索を用いる $ins->Duplicate=false; //漢字検索フィールドの重複を省かない $ins->TableName="JSONファイル名"; $ins->searchValue=$value; $ins->searchField="漢字検索フィールド"; $ins->searchFieldKana="かな検索フィールド"; $ins->sendFilteredJSON(); } /* ##################### クラス ########################### */ class TableData{ public $IsSQL; public $PDOName; public $UseKana; public $TableName; public $searchValue; public $searchField; public $searchFieldKana; function __construct(){ $this->IsSQL = true; $this->UseKana = false; $this->UseKata = true; $this->Duplicate=true; $this->PDOName = "./rs/pdoclass.php"; } function sendFilteredJSON(){$this->echoJSON($this->getFilteredArray());} function getFilteredArray(){return $this->_getFilteredArray($this->getAsArray());} function _getFilteredArray($asArray){ if($this->UseKana && preg_match("/^[ぁ-んー]+$/u",$this->searchValue)){$outArray= $this->getFilArrayfromKana($asArray,$this->searchValue,$this->searchField,$this->searchFieldKana);} else{ $outArray= $this->getFilArray($asArray,$this->searchValue,$this->searchField);} $outArray= $this->getDeletedArray($outArray, $this->searchField); return $outArray; } function getAsArray(){if($this->IsSQL){return $this->getAsArrayfromSQL("SELECT * FROM ".$this->TableName);}else{return json_decode(file_get_contents($this->TableName, false),true);}} function getAsArrayfromSQL($sql){require_once $this->PDOName;$obj=new connect();$hoge=$obj->pdo();$AsArray=$hoge->prepare($sql);$AsArray->execute();return $AsArray->fetchAll(PDO::FETCH_ASSOC);} function getFilArray($array,$value,$col) {return array_filter($array, function($v) use ($value,$col) {return (strpos($v[$col],$value)) !== false;});} function getFilArrayfromKana($array,$value,$col,$colkana) {return array_filter($array, function($v) use ($value,$col,$colkana) {return (strpos($v[$colkana],$this->getHalfKata($value))) !== false;});} function getHalfKata($str){ if($this->UseKata){return mb_convert_kana($str,"h");}else{return $str;}} function echoJSON($array){ header("Content-type: application/json; charset=UTF-8"); echo json_encode($array); } function getDeletedArray($array, $col){if($this->Duplicate){$tmp = []; foreach ($array as $value){if (!in_array($value[$col], $tmp)) {$tmp[] = $value[$col];$outArray[] = $value;} } return $outArray; }else{ return $array;}} } ?> 次に Javascript を示します。 /* ################### イベント ###############*/ $(document).ready(function(){ $("#input_town").keyup(function(){ $("#select_town").children().remove(); data=getArray($(this).val(),"getTown"); Object.keys(data).forEach(key => $('#select_town').append('<option>'+data[key]["表示させたいフィールド名"]+"</option>")); }); });//$(document).ready(function(){ の閉じ /* ################### イベントend ###############*/ /* ################### Ajax ###############*/ function getArray(value,fnc){ var data; $.ajax({ async: false, type: "POST", url: "xxx.php", data: { "value" : value,"fnc":fnc}, dataType : "json", success: function(res){ data=res; } }); return data; } /* ################### Ajax end ###############*/ 次にhtml <body> <input type="text" id="input_town"> <select name="name" id="select_town" ></select> </body> 郵便番号の例外を手で正規化 郵政のデータ(CSV)を見ると以下のように「同じ町名だけれど郵便番号が異なる町」があることがわかる。 通常は「町名だけ」が入力されているカラムに、このような番地を含む変則的なデータが打ち込まれている。つまり、町名が打ち込まれていないカラムがある。 なのでこれを他のと同じフォーマットにしたいが、プログラム的にこのような例外の場合、「番地」から「郵便番号」を導くような処理は現実的ではない(各例外別にいちいちプログラム処理を書かねばならない)。 かといって代表的な郵便番号を表示すれば「間違った郵便番号」を出していることになる。 つまり、このような例外の場合、さしあたって郵便番号を出さないのが無難である。逆にいえばこの場合、「郵便番号から町名」は一意的なのである。 そうするにはどうするか?やりかたは人それぞれあるが、 zip 列を複数作るのもよいではないか。 たとえば熊本県内の場合、「最も郵便番号を多くもつ町」は、4つの郵便番号を持っている(上記の画像の例)。 10とか20とかならやりきれないが4つ程度であれば、 zip カラムを4つ作り、 一意的な郵便番号は zip に入れて、あとは zip2, zip3,, zip4 に入れておくのである。 (つまり、ほとんどの町は zip にしか郵便番号がはいっていない。たとえば郵便番号が複数ある河浦町今富であれば zip, zip2, zip3, zip4, town 8631403, 8631203, , ,河浦町今富 というわけである。 手で正規化とは原始的ではあるが、あてもなく例外に対応するようなプログラムで書くより、割合にして1%もないのだから手でやったほうが早いし、確実である(参照データであるし)。 ちなみに市販のソフトはどうしているであろうか。そもそも市販のソフトは郵便番号から住所を導くことはあっても、住所から郵便番号を導くものは少ないし、例外データの場合は「郵便番号が表示されない」場合が多いのではないか。 上記のように zipを複数作っておけば、 たとえば、部分一致で「かわうら」という文字を入力したとき、部分一致でフィールドIDが決定されたとき、 まず zip4 をみて空なら zip3 をみて空なら zip2 をみて、これは空ではないから、「zip」「zip2」をセレクトボックスに一覧表示するとか、そんな処理も可能なわけである。 郵便番号が1つしかない多くの町の場合、 zip4, zip3, zip2 はからであるので、 1つしかないからテキストボックスに入れるとか、そのあたりは仕様次第であるが、対応できるわけである。 間違っても例外データの場合、上記の画像にかかれているように「○○番地~○番地は郵便番号・・」なんて処理をプログラムで書くことなんてないわけである。なぜなら、そのような例外は熊本県だけでも数十あるわけで、全国なら数千あるわけである。 数千の例外に対して個別に処理を書きようがないわけであるから、対応できないとういわけである。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP8.1】PHP8.1の新機能

PHP8.1 / PHP8.0 / PHP7.4 2021/07/20、PHP8.1がフィーチャーフリーズしました。 言語機能に関わるような機能の追加・変更が締め切られたということです。 今後はデバッグを繰り返しながら完成度を高めていき、2021/11/25にPHP8.1.0がリリースされる予定です。 というわけでPHP8.1で実装されるRFCを見てみましょう。 RFC Fibers 賛成50反対14で受理。 Fiberです。 PHPで非同期コードを書けるようになります。 $fiber = new Fiber(function (): void { $value = Fiber::suspend('fiber'); echo "レジュームした。$value: ", $value, "\n"; }); $value = $fiber->start(); echo "一時停止した。$value: ", $value, "\n"; $fiber->resume('test'); // 実行結果 一時停止した。$value: fiber レジュームした。$value: test イベントループにPromiseといった、これまでのPHPでは困難だった処理がついに書けるようになります。 基本的に一般ユーザ向けの機能ではないので、ライブラリが対応するのを待つ必要がありますが、ノンブロッキングなコードをPHPでも書けるようになりそうですね。 Pure intersection types 賛成30反対3で受理。 交差型です。 PHP8.0で追加されたUNION型に引き続き、交差型も使用可能になります。 class A { // UNION型 $aはTraversableもしくはCountableである private Traversable|Countable $a; // 交差型 $bはTraversableかつCountableである private Traversable&Countable $b; } これで好きなだけ型パズルが作れるようになりますね。 Enumerations 賛成44反対7で受理。 列挙型です。 enum Suit { case Hearts; case Diamonds; case Clubs; case Spades; } $val = Suit::Diamonds; まずはENUMが実装されましたが、元々のRFCは最終的に代数的データ型の実装を目指す巨大な計画です。 ENUMはクラスを下敷きに作られているため、個別にメソッドを実装したりとかなり複雑な作り込みもできるようになっています。 個人的にはENUMがなくてどうにもならないという事態に遭遇したことがないのでどのくらい有用かはよくわかりませんが、古来よりENUMをPHPに実装する試みが何度も行われているくらいなので、きっと需要も大きいのでしょう。 noreturn type 賛成42反対11で受理。 never型です。 function foo():never{ exit; } 途中でexitしたり常に例外を出すような、呼び出し元に返らない関数において返り値として書ける型neverが追加されます。 Readonly properties 2.0 readonlyアクセス修飾子です。 class Test { public readonly string $prop; public function __construct(string $prop) { $this->prop = $prop; } } $test = new Test("foobar"); // 読み込みはOK var_dump($test->prop); // string(6) "foobar" // 書き込みはNG $test->prop = "foobar"; // Error: Cannot modify readonly property Test::$prop 一度だけ初期化が可能で、その後変更されないことを保証するプロパティです。 protectedやprivateを使う理由の98%は『読まれてもいいけど書き込ませたくない』なので、そのユースケースを万事解決してくれます。 ちなみに上のクラスTestは、今ではここまで省略して書くことが可能です。 class Test { public function __construct(public readonly string $prop) {} } Deprecate implicit non-integer-compatible float to int conversions 賛成29反対0で受理。 floatからintへの暗黙の型変換にE_DEPRECATEDが発生するようになります。 intからfloat、floatからstringといった変換は、基本的に情報の損失が発生しません。 しかしfloatからintへの変換はそうではありません。 function toInt(int ...$arg){ var_dump($arg); } function toFloat(float ...$arg){ var_dump($arg); } function toString(string ...$arg){ var_dump($arg); } toInt(1, '1', 1.5, '1.5'); // [1, 1, 1, 1] toFloat(1, '1', 1.5, '1.5'); // [1, 1, 1.5, 1.5] toString(1, '1', 1.5, '1.5'); // ['1', '1', '1.5', '1.5'] intに変換したところだけ情報が抜け落ちてしまっています。 そのため、この暗黙的な変換については今後警告を出すようにします。 明示的な変換(int)1.5やintval(1.5)については、これまでどおり問題なく変換可能です。 もっとも、現在でもdeclare(strict_types=1)を付けていればこの変換はTypeErrorになるので、strictな書き方をしている限りは何の影響もありません。 Final class constants 賛成29反対4で受理。 finalクラス定数です。 class FOO{ final public const HOGE = 1; } 継承したクラスで上書き変更できないことが保証される定数を書けるようになります。 これによって、クラス、メソッド、定数にfinalを書けるようになりました。 プロパティには書けませんが、上記のreadonlyがちょうど代替になるでしょう。 New in initializers 賛成43反対2で受理。 引数デフォルト値でnewできるようになります。 class Test { // PHP8.0まで private Logger $logger; public function __construct( ?Logger $logger = null, ){ $this->logger = $logger ?? new NullLogger; } // PHP8.1以降 public function __construct( private Logger $logger = new NullLogger, ){} } これまで引数デフォルト値にはスカラ値しか置けなかったので、一度nullで受けてからメソッド内で分岐するという不自然な書き方をしないといけなかったのですが、これが自然に書けるようになります。 Array unpacking with string keys 賛成50反対0で受理。 引数アンパックの文字列キー対応です。 PHP7.4で実装された引数の配列アンパックですが、文字列キーの配列には非対応でした。 $array1 = [1]; $array2 = ['a'=>2]; $array = [...$array1, ...$array2]; // PHP8.0までFatal Error: Cannot unpack array with string keys これは、当時は文字列キーの配列に対応する意味がなかったからです。 ところがこの後、PHP8.0で名前付き引数が実装されたことで文字列キーに俄然有用な意味が出ることになりました。 そこで文字列キーのアンパックを許可します。 call(...["a" => 1]); // ↑は↓になる call(a: 1); 引数の数が多い関数に、引数を連想配列のまま渡せるようになるので便利ですね。 $args = [ 'string' => $str, 'flags' => \ENT_QUOTES, 'encoding' => 'UTF-8', ]; htmlspecialchars(...$args); どういうことってこんなふうに書けるってことですよ。 Deprecate passing null to non-nullable arguments of internal functions 賛成46反対0で受理。 nullを許容しない内部関数への引数nullをE_DEPRECATEDにします。 たとえばstrlenのシグネチャはstrlen(string $string): intであり、引数はnull許容型ではありません。 しかし、普通にnullを渡せるしエラーも出ません。 strlen(null); // エラー出ない function my_strlen(string $string): int{ return strlen($string); } my_strlen(null); // TypeError Argument 1 passed to my_strlen() must be of the type string, null given ユーザ定義関数だときちんとTypeErrorになります。 しかし内部関数は、歴史的理由もあって引数に甘い判定をしがちです。 この矛盾を解消するためのRFCです。 ただ、いきなりTypeErrorにすると大混乱なので、PHP8.1ではE_DEPRECATEDとし、PHP9かそれ以降でユーザ定義関数と同じくTypeErrorにします。 Add return type declarations for internal methods 賛成17反対7で受理。 内部クラスをextendsした際に、返り値の型宣言をチェックするようにします。 class MyDateTime extends DateTime { public function modify(string $modifier):int { return 1; } } $dt = new MyDateTime(); $dt->modify('modifier'); DateTime::modifyの返り値はDateTime|falseですが、extendsした際に全く無視してintとか付けても普通に動きますしエラーも出ません。 これを互換しない型についてはE_DEPRECATEDを出すようにします。 PHP9ではTypeErrorになります。 さきほどのRFC同様、組み込み関数は甘めの判定になっているところが多いです。 ユーザ定義クラスをextendsした場合は、このコードは当然TypeErrorになります。 First-class callable syntax 賛成44反対0で受理。 Closure::fromCallableの新文法です。 // 同じ $fn = Closure::fromCallable('strlen'); $fn = strlen(...); // 同じ $fn = Closure::fromCallable([$this, 'method']); $fn = $this->method(...) // 同じ $fn = Closure::fromCallable([Foo::class, 'method']); $fn = Foo::method(...); ...は省略記号とかではなく、このままの文字です。 まあ、ただの糖衣構文です。 これで何がうれしいのかというと、文字列で渡さないので静的解析が可能になるというところです。 しかしこの構文がいきなり出てきたら、アンパックと見間違って混乱する自信がある。 Add IntlDatePatternGenerator 賛成10反対0で受理。 ローカライズされた日付フォーマッタを導入します。 $skeleton = "YYYYMMdd"; $today = \DateTimeImmutable::createFromFormat('Y-m-d', '2021-04-24'); $dtpg = new \IntlDatePatternGenerator("de_DE"); $pattern = $dtpg->getBestPattern($skeleton); echo "de: ", \IntlDateFormatter::formatObject($today, $pattern, "de_DE"), "\n"; // de: 24.04.2021 $dtpg = new \IntlDatePatternGenerator("en_US"); $pattern = $dtpg->getBestPattern($skeleton), "\n"; echo "en: ", \IntlDateFormatter::formatObject($today, $pattern, "en_US"), "\n"; // en: 04/24/2021 これは…どうなんだ? まあPHPのintlはICUのそれをラップしているだけなので、PHPだからどうとか言われても困りますけどね。 getBestPatternとかいう名前も実は元のままだったりします。 しかしintlの情報とか使用例とかって全然出てこないので、ja_JPだったら2021/04/24になるのか2021年4月24日になるのか、そこらへんもよくわからないな。 Add array_is_list(array $array): bool 賛成41反対1で受理。 関数array_is_listを追加します。 キーが0始まりで抜けの無い配列である場合にのみ、trueになります。 array_is_list([1, 2]); // true array_is_list([1=>1, 2]); // false array_is_list([1, 2=>2]); // false array_is_list(['a'=>1, 2]); // false PHPで配列と呼ばれているものは実際は順序付きハッシュであり、他言語で言うところの配列というものは存在しません。 この関数を使えば、他言語で言うところの配列と同じものであるか否かを判定できるようになります。 まあ純粋な配列が使いたければSplFixedArrayとか使えばいいですし、そもそもPHPでは純粋な配列と抜けのある配列を区別する意味が全くないので、ユーザランドでの出番は多くないと思います。 Explicit octal integer literal notation 賛成33反対0で受理。 8進数の基数表示です。 $a = 16; // 10進数 $a = 0x10; // 16進数 $a = 0b00010000; // 2進数 $a = 020; // 8進数 8進数だけ接頭辞がなくてわかりにくいですね。 そこで0oによる8進数表記をサポートします。 $a = 0o20; // PHP8.1以降OK 既存の書き方については、当面は削除される予定はありません。 Restrict $GLOBALS usage 賛成48反対0で受理。 $GLOBALSの内部処理を変更するRFCです。 $a = 1; $globals = $GLOBALS; // 値をコピー $globals['a'] = 2; var_dump($a); // int(2) 値をコピーしただけのはずなのに、何故か元の値も変わってしまっています。 このように$GLOBALSには色々と特殊な仕様が存在しており、そのせいで内部構成が複雑になり、パフォーマンスの問題も発生しているそうです。 そこで、このような特殊な仕様をなるべく排除します。 通常の使い方での互換性は概ね維持されますが、リファレンスを駆使した変なコードは動かなくなるみたいです。 foreach ($GLOBALS as $var => $_) $$var =& $GLOBALS[$var]; // 動かなくなる まあ、普通はそもそも$GLOBALSが目に入った時点で拒否反応が出ますよね。 Change Default mysqli Error Mode 賛成21反対9で受理。 mysqliのエラーモードのデフォルトが例外になります。 PHP8.0までデフォルト値はMYSQLI_REPORT_OFFであり、あえて設定変更しないかぎり黙って死ぬという困った仕様でした。 PHP8.1以降デフォルト値はMYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICTになります。 PHP8.0でPDOのエラーモードデフォルトも例外になったので、それに合わせたものとなります。 Add fetch_column method to mysqli 賛成18反対2で受理。 mysqli_result::fetch_columnメソッドです。 mysqliにはfetch関数が色々あるのに、何故かfetch_columnがなかったので追加されます。 ただ個人的にはPDOでもfetchColumnを使ったことないし、ひとつのカラムだけ取り出すという需要がよくわかりません。 Mysqli bind in execute 賛成32反対0で受理。 mysqli::executeが実行時引数渡しに対応します。 // PDO 事前渡し $stmt = $pdo->prepare('INSERT INTO users(id, name) VALUES(?,?)'); $stmt->bindParam(0, $id); $stmt->bindParam(1, $name); $stmt->execute(); // PDO 実行時渡し $stmt = $pdo->prepare('INSERT INTO users(id, name) VALUES(?,?)'); $stmt->execute([$id, $name]); // mysqli 事前渡し $stmt = $mysqli->prepare('INSERT INTO users(id, name) VALUES(?,?)'); $stmt->bind_param('ss', $id, $name); $stmt->execute(); mysqliは何故か引数の実行時渡しに対応していませんでした。 PHP8.1以降は、PDO同様実行時に渡せるようになります。 // mysqli 実行時渡し PHP8.1以降 $stmt = $mysqli->prepare('INSERT INTO users(id, name) VALUES(?,?)'); $stmt->execute([$id, $name]); 個人的には完全にPDOしか使わないので、mysqliにいまさら色々追加されてもなあって感じが否めない。 fsync() Function 賛成30反対1で受理。 関数fsyncを追加します。 ソースを見てみると単にC言語のfsyncのラッパーでした。 よくわからないので参考URLとかを読んでみるに、fwriteなどの書き込み関数は、実は呼んだ時点でディスクに書き込み完了しているわけではなく、どこかにキャッシュされているだけなので、直後にプログラムが落ちたら書き込まれずに消滅してしまう危険性がある。 そこでキャッシュを出力する関数としてfflushやfsyncやfdatasyncが存在し、fsyncであれば実際にディスクに書き込み完了したところまで確認する。 ということのようです。 PHPにはfflushは存在しましたが、これまでは無かったfsyncを追加します。 あとRFCにははっきり書かれていませんが、プルリクにはしれっとfdatasyncも入ってました。 Phasing out Serializable 賛成36反対0で受理。 SerializableをE_DEPRECATEDにします。 class HOGE implements Serializable{ // Serializable public function serialize() { return serialize($this->data); } public function unserialize($data) { $this->data = unserialize($data); } // マジックメソッド public function __serialize() { return serialize($this->data); } public function __unserialize($data) { $this->data = unserialize($data); } } PHP7.4でマジックメソッド__serialize/__unserializeが実装され、Serializableより優先されるようになりました。 上記コードはPHP7.4以降__serialize/__unserializeだけが実行され、serialize/unserializeは無視されます。 今回E_DEPRECATEDになる条件は、Serializableだけが実装されていて、__serialize/__unserializeが実装されていない場合です。 両方とも実装されている場合は、PHP8.1でも警告は出ないようです。 両方とも実装されていない場合は、もちろん今までどおり何も出ません。 Static variables in inherited methods 賛成38反対0で受理。 静的変数を継承したときの挙動が微妙に変更になります。 class A { public static function counter() { static $i = 0; return ++$i; } } class B extends A {} var_dump(A::counter()); // int(1) var_dump(A::counter()); // int(2) var_dump(B::counter()); // <PHP8.1:int(1) >=PHP8.1:int(3) var_dump(B::counter()); // <PHP8.1:int(2) >=PHP8.1:int(4) 静的変数は、同じクラスであれば同じ変数、異なるクラスであれば別の変数になる、というよくわからない状態でした。 これを継承に関わらず常に同じ変数を見るようにします。 変更後は、クラス変数と同じ挙動になります。 class A { static $i = 0; public static function counter() { return ++self::$i; } } class B extends A {} var_dump(A::counter()); // int(1) var_dump(A::counter()); // int(2) var_dump(B::counter()); // int(3) var_dump(B::counter()); // int(4) 静的変数とクラス変数でなぜか動作が微妙に異なっていたので、この動作を合わせるためのものです。 そもそもなんで異なっていたんだ。 Make reflection setAccessible() no-op 賛成31反対0で受理。 ReflectionPropertyおよびReflectionMethodにおいて、デフォルトでsetAccessible(true)になります。 class HOGE{ private static int $foo = 1; private static function bar() { return 2; } } // ReflectionProperty $p = new ReflectionProperty(HOGE::class, 'foo'); $p->setAccessible(true); $p->getValue(); // 1 // ReflectionMethod $p = new ReflectionMethod(HOGE::class, 'bar'); $p->setAccessible(true); $p->invoke(); // 2 このsetAccessible、どうせ毎回必ず指定するんだから最初からデフォルトでええやん、という主張。 これによって、同じコードが(new ReflectionProperty(HOGE::class, 'foo'))->getValue();みたいにさくっと書けるようになります。 そしてsetAccessibleは実質的に何もしなくなります。 まあ便利といえば便利だけど、もっと評価が分かれてもよさそうな提案なのに、全員賛成ってのは少々びっくり。 Deprecate autovivification on false 賛成34反対2で受理。 falseから配列の自動生成を禁止します。 // undefinedから $arr[] = 'some value'; $arr['doesNotExist'][] = 2; // ['some value', 'doesNotExist'=>[2]] // nullから $arr = null; $arr[] = 2; // [2] // falseから $arr = false; $arr[] = 2; // [2] PHPでは、このように何もないところにいきなり配列値を突っ込むと、自動的に配列ができあがります。 このうち、falseだけはPHP8.1から自動生成を禁止します。 これだけだと何でfalseだけ?って思いますが、実は''や0やtrueからの配列自動生成は既に前から禁止されているので、そちらのスカラー型の動作に合わせたということです。 むしろundefinedとnullが例外です。 // trueから $arr = true; $arr[] = 2; // Warning: Cannot use a scalar value as an array Deprecations for PHP 8.1 様々な古い機能や書き方について、PHP8.1でDepreatedにし、PHP9で削除するという提案。 数が多いのでここでは紹介しませんが、ほとんどは聞いたこともない関数や元より動いていなかった設定などです。 たとえばdate_sunriseとdate_sunsetを削除してdate_sun_infoに集約します。 個人的に気になったのはstrftimeくらいですかね。 ほとんどのユーザにとっては影響はないと思われます。 感想 PHP8.0やPHP7.4に比べるとそこまで多いというわけではありませんが、それでも様々な機能追加や改善がなされています。 今回の目玉はFiberと型関連でしょうか。 Fiberは『上から順に実行する』というPHPの原則を破る、大きな可能性を秘めた存在です。 今後のライブラリの対応次第では、有力な機能となることでしょう。 また型については、交差型・never型・列挙型の追加、継承時の挙動厳格化などが含まれています。 PHPは7以降、型関連の実装・機能追加が相次いで行われてきました。 今ではもはや、下手な静的型付け言語よりずっと厳格な型運用を行うことすら可能になっています。 あとArray unpacking with string keysは、これまでと書式ががらっと変わるのでPHP8.1以前と両用していると導入は難しそうですが、使い慣れると非常に便利になりそうです。 ということで、年末にリリース予定のPHP8.1をみんなも是非やりましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 2つの日時の差の「時・分・秒」を取得する。タイムカードの打刻時刻など

概要 PHPの質問に、タイムカード打刻時刻datetimeから「時・分・秒」を求める方法の質問があったので、ぐぐったところ、差の秒数をgmdateに渡すサンプルやDateTime::diff()を使用するサンプルなどが出てきたが、どれも私はイマイチに感じたのでシンプルに書いた サンプル 出勤時刻 '2022-08-12 17:18:30' 休憩開始 '2022-08-12 23:30:00' 休憩終了 '2022-08-13 00:35:00' 退勤時刻 '2022-08-13 20:20:18' ※退勤時刻は計算の確認のため、 あえて24時間オーバーさせてます。 test.php <?php /** * Class hmsClass */ class hmsClass { /** * @var int */ public $h = 0; /** * @var int */ public $m = 0; /** * @var int */ public $s = 0; /** * 秒を「時、分、秒」へ * * @param int $time * @return false|static */ public static function gethms($time) { // 数値でない時 if (! preg_match('/^[\-+]?[0-9]+$/', strval($time))) { trigger_error(__METHOD__.'(): Argument #1 ($time) must be number', E_USER_WARNING); return false; } $time = intval($time); $obj = new static(); $obj->h = (int)($time / 3600); $obj->m = ($time / 60) % 60; $obj->s = $time % 60; return $obj; } } // 打刻時刻 出勤時刻(1970年1月1日午前0時0分0秒からの経過秒) $begin_time = strtotime('2022-08-12 17:18:30'); // 打刻時刻 休憩開始時刻(1970年1月1日午前0時0分0秒からの経過秒) $break_begin_time = strtotime('2022-08-12 23:30:00'); // 打刻時刻 休憩終了時刻(1970年1月1日午前0時0分0秒からの経過秒) $break_end_time = strtotime('2022-08-13 00:35:00'); // 打刻時刻 退勤時刻(1970年1月1日午前0時0分0秒からの経過秒) $end_time = strtotime('2022-08-13 20:20:18'); // 休憩時間(秒) = 休憩開始時刻(秒) - 休憩終了時刻(秒) $break_time = $break_end_time - $break_begin_time; $hms = hmsClass::gethms($break_time); echo('$hms='.print_r($hms, true))."\n"; // $hms=stdClass hmsect // ( // [h] => 1 // [m] => 5 // [s] => 0 // ) // 労働時間(秒) = 退勤時刻(秒) - 出社時刻(秒) - 休憩時間(秒) $work_time = $end_time - $begin_time - $break_time; $hms = hmsClass::gethms($work_time); echo('$hms='.print_r($hms, true))."\n"; // $hms=stdClass hmsect // ( // [h] => 25 // [m] => 56 // [s] => 48 // ) テスト コメントで負数の時のコメントをいただいたので、テストを書いてみました。 gethmsTest.php <?php use PHPUnit\Framework\TestCase; require 'gethms.php'; /** * Class gethmsTest */ class gethmsTest extends TestCase { public function test() { $hms = hmsClass::gethms(0); $this->assertSame(0, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(0, $hms->s); $hms = hmsClass::gethms(1); $this->assertSame(0, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(1, $hms->s); $hms = hmsClass::gethms(-1); $this->assertSame(0, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(-1, $hms->s); $hms = hmsClass::gethms(3600); $this->assertSame(1, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(0, $hms->s); $hms = hmsClass::gethms(-3600); $this->assertSame(-1, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(0, $hms->s); $hms = hmsClass::gethms(99999); $this->assertSame(27, $hms->h); $this->assertSame(46, $hms->m); $this->assertSame(39, $hms->s); $hms = hmsClass::gethms(-99999); $this->assertSame(-27, $hms->h); $this->assertSame(-46, $hms->m); $this->assertSame(-39, $hms->s); $hms = hmsClass::gethms('1'); $this->assertSame(0, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(1, $hms->s); $hms = hmsClass::gethms('-1'); $this->assertSame(0, $hms->h); $this->assertSame(0, $hms->m); $this->assertSame(-1, $hms->s); // 数字ではない時は例外 $e = null; try { $hms = hmsClass::gethms('string'); } catch (\Exception $e) { // trigger_errorメッセージの確認 $this->assertSame('hmsClass::gethms(): Argument #1 ($time) must be number', $e->getMessage()); } // 例外が発生した事を確認 $this->assertIsObject($e); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 2つの時刻の差の時分秒を取得する。タイムカードの打刻時刻など

概要 PHPの質問に、タイムカード打刻時刻datetimeから「時・分・秒」を求める方法の質問があったので、ぐぐったところ、差の秒数をgmdateに渡すサンプルやDateTime::diff()を使用するサンプルなどが出てきたが、どれも私はイマイチに感じたのでシンプルに書いた サンプル 出勤時刻 '2022-08-12 17:18:30' 休憩開始 '2022-08-12 23:30:00' 休憩終了 '2022-08-13 00:35:00' 退勤時刻 '2022-08-13 20:20:18' ※退勤時刻は計算の確認のため、 あえて24時間オーバーさせてます。 test.php <?php /** * 秒を「時、分、秒」へ * @param int $time * @return \stdClass */ function gethms($time) { $obj = new \stdClass(); $obj->h = floor($time / 3600); $obj->m = ($time / 60) % 60; $obj->s = $time % 60; return $obj; } // 打刻時刻 出勤時刻(1970年1月1日午前0時0分0秒からの経過秒) $begin_time = strtotime('2022-08-12 17:18:30'); // 打刻時刻 休憩開始時刻(1970年1月1日午前0時0分0秒からの経過秒) $break_begin_time = strtotime('2022-08-12 23:30:00'); // 打刻時刻 休憩終了時刻(1970年1月1日午前0時0分0秒からの経過秒) $break_end_time = strtotime('2022-08-13 00:35:00'); // 打刻時刻 退勤時刻(1970年1月1日午前0時0分0秒からの経過秒) $end_time = strtotime('2022-08-13 20:20:18'); // 休憩時間(秒) = 休憩開始時刻(秒) - 休憩終了時刻(秒) $break_time = $break_end_time - $break_begin_time; $obj = gethms($break_time); echo('$obj='.print_r($obj, true))."\n"; // $obj=stdClass Object // ( // [h] => 1 // [m] => 5 // [s] => 0 // ) // 労働時間(秒) = 退勤時刻(秒) - 出社時刻(秒) - 休憩時間(秒) $work_time = $end_time - $begin_time - $break_time; $obj = gethms($work_time); echo('$obj='.print_r($obj, true))."\n"; // $obj=stdClass Object // ( // [h] => 25 // [m] => 56 // [s] => 48 // )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP脳のが故にJavaScriptでarray_pad的関数がなくて泣いてる君へ

はじめに こんにちは。SBI証券での投信積立にクレカが使用できるようになったので、対象の三井住友カードを申し込みました。 クレカで積立できるとポイントたまるのでお得です!皆様もぜひ! さて、今回は、JavaScriptでPHPのarray_padをしたいときがあり、生み出したコードを共有します。 コピペで使えるので良ければ使ってくださいませ PHPのarray_pad関数とは 配列が指定長に満たない場合、指定長まで指定の値で埋めることができる関数です。 リファレンスこちら↓ $ php --rf array_pad Function [ <internal:standard> function array_pad ] { - Parameters [3] { Parameter #0 [ <required> array $array ] Parameter #1 [ <required> int $length ] Parameter #2 [ <required> mixed $value ] } - Return [ array ] } 使用例 <?php $array = [12, 10, 9]; $length = 5; $val = 'hello'; $result = array_pad($input, $length, $val); // [12, 10, 9, 'hello', 'hello'] ?> JavaScriptでarray_padしたい! const array = [12, 10, 9] const length = 5 const val = 'hello' const result = [ ...array, ...[...Array(length - array.length)].map(e => val) ] // [12, 10, 9, 'hello', 'hello'] おわりに いろいろやり方あると思いますが、個人的に一番シンプルにできた気がしてます それでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP脳が故にJavaScriptでarray_pad的関数がなくて泣いてる君へ

はじめに こんにちは。SBI証券での投信積立にクレカが使用できるようになったので、対象の三井住友カードを申し込みました。 クレカで積立できるとポイントたまるのでお得です!皆様もぜひ! さて、今回は、JavaScriptでPHPのarray_padをしたいときがあり、生み出したコードを共有します。 コピペで使えるので良ければ使ってくださいませ PHPのarray_pad関数とは 配列が指定長に満たない場合、指定長まで指定の値で埋めることができる関数です。 リファレンスこちら↓ $ php --rf array_pad Function [ <internal:standard> function array_pad ] { - Parameters [3] { Parameter #0 [ <required> array $array ] Parameter #1 [ <required> int $length ] Parameter #2 [ <required> mixed $value ] } - Return [ array ] } 使用例 <?php $array = [12, 10, 9]; $length = 5; $val = 'hello'; $result = array_pad($array, $length, $val); // [12, 10, 9, 'hello', 'hello'] ?> JavaScriptでarray_padしたい! const array = [12, 10, 9] const length = 5 const val = 'hello' const result = [ ...array, ...[...Array(length - array.length)].map(e => val) ] // [12, 10, 9, 'hello', 'hello'] 2021/07/26(月) 追記 コメントをいただきまして、こちらの方がスマートで素敵なコードになります ぜひこちらを使用してくださいませ! concat使うならスプレッド構文は不要でしたね。 const result = array.concat(Array(length - array.length).fill(val)); const array = [12, 10, 9] const length = 5 const val = 'hello' const result = array.concat(Array(length - array.length).fill(val)); おわりに いろいろやり方あると思いますが、個人的に一番シンプルにできた気がしてます それでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

composer のコマンドを勉強してみた

composer は Laravel インストールする時に使った事はあるけれど、特に意識することなく(何も考えずに)使ってました・・・ そんな中、Laravel の carbon パッケージを調べる事があり、ふと「このcarbonってバージョンいくつなんだろう」と疑問に思った事が composer のコマンド使ってみたきっかけです。 備忘録的な意味合いが強いのですが、誰か、何かの役に立てばうれしいです。 パッケージの一覧を表示する composer show -i You are using the deprecated option "installed". Only installed packages are shown by default now. The --all option can be used to show all packages. ~~~ 略 ~~~ monolog/monolog 2.2.0 Sends your logs to files, sockets, inboxes, databases and various web services nesbot/carbon 2.46.0 An API extension for DateTime that supports 281 different languages. ~~~ 略 ~~~ いろいろと出力されました。carbon のバージョンは 2.46.0 のようです。 composer update と composer install よくcomposer updateは本番環境では実行しない事!なんてことを聞きますが何でですかね?あまり知らなかったのですが、今回調べた事を記します。 まずはcomposer updateとcomposer installの対比表 実行コマンド 対 composer.json 対 composer.lock composer install 参照のみ 参照のみ composer update 参照のみ 書き込みあり     composer install の動き 1. composer.lock が存在すればファイル内に記されたパッケージのバージョン情報を取得する 2. composer.json ファイル内に記載されたパッケージをcomposer.lockに記されたバージョン情報に従ってパッケージのダウンロード・インストールを行う composer.lockとcomposer.jsonは読み込みのみ     composer update の動き 1. composer.json ファイル内に記載されたパッケージとバージョン情報に従ってダウンロード・インストールを行う 2. インストールしたパッケージ情報でcomposer.lock を更新する composer.lockは書き込み     composer.lockはチーム内などで共有し、必要のないパッケージのバージョンアップを行わない為に使用されます。これを使用する事で意図しないバージョンアップによる不具合、コンフリクトを防ぎます。 そのcomposer.lockをcomposer updateでは更新します。 なので、通常の流れでは 開発環境でcomposer updateを実行 composer.lock composer.jsonファイルを共有 本番環境で共有したcomposer.lockを使用してcomposer installを実行 という事になるのかな?? 実際に本番環境でcomposer updateを行って、意図しないバージョンまでアップデートされ、その結果、稼働してたシステムが動かなくなったというケースを見たことがあります。       とりあえずこんなところで 勉強中なので必要があれば、追記、修正していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8 API Resource Queryの効率化

Laravel8 API Resource Queryの効率化 Laravel8のリレーションシップのQueryをDebugしてみたところ レコードごとのQueryを実行していたのでQueryを効率化する方法について調べました。 (前提はUserとPostの1:N関係) まずはLaravelのLoadigの種類について調査 Lazy Loading(怠惰な読込み) LaravelのデフォルトのLoading方式だが、N+1問題を起こすLoading方式 ※N+1問題とは Lazy LoadingのSQL例 Routeの例 api.php use App\Http\Resources\PostCollection; use App\Models\Post; Route::get('/posts', function () { return new PostCollection(Post::all()); }); SQL実行例 postの例 SELECT * FROM posts; SELECT * FROM users WHERE id = 1; SELECT * FROM users WHERE id = 2; SELECT * FROM users WHERE id = 3; ... Eager Loading(熱心な読込み) Routeの例 api.php use App\Http\Resources\PostResource; use App\Models\Post; Route::get('/posts', function () { $posts = Post::with(['users']); return PostResource::collection($posts->paginate(50))->response(); }); PostResource.php <?php namespace App\Http\Resources\Post; use Illuminate\Http\Resources\Json\JsonResource; use App\Http\Resources\UserResource; class CustomerResource extends JsonResource { /** * Transform the resource collection into an array. * * @param \Illuminate\Http\Request $request * @return array */ public function toArray($request) { return [ 'id' => $this->id, 'users' => UserResource::collection($this->whenLoaded(relationship:'users')), ]; } } SQL実行例 postの例 select * from `posts` where `users`.`user_id` in (1, 2, 3) 結論 レコード数分のQueryが1Queryにまとまるのでその分のSqlの接続数が減り、 アプリケーションのパフォーマンスが上がります。 API Resourceのほうには適応したほういいでしょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む