20210107のPHPに関する記事は15件です。

[EC-CUBE4]serviceファイル内でユーザのログイン状態を判別する方法

環境

EC-CUBE 4.0.5
symfony 3.4.42
php 7.3

実行方法

// appを取得
$app = \Eccube\Application::getInstance();
// user情報を取得
$user = $app['user'];
// ゲストユーザは"anon."が格納される
if ($user != "anon.") {

  // 会員限定で行いたい処理

}

これでログイン状態のユーザ(会員)を判別して処理を行えるうようになります。

参考に

isGranted('ROLE_USER')は使えない

こちらはAbstractControllerを継承しているController内でしか使えないです。
twig内で使うようなis_granted('ROLE_USER')も使えません。

他の実行例

こちらの記事にある
$this->app->user()を使って取れる場合もあるようですが、私の環境では取れませんでした。

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

LaravelのPaginateでエラーが出たワケ

はじめに

初めてLaravelを触ってみました。プログラミング初心者らしすぎる凡ミスですが、記録的として残したいと思います。

環境

PHP 7.4
Laravel 6.20.8

エラー内容

BadMethodCallException
Call to undefined method App\XXX(model)::links() (View: /work/resources/views/XXX.blade.php)

Paginateメソッドについて

Laravelにはページネーションがデフォルトで備わっています。
例えば

UserController
$users = User::orderBy('created_at', 'desc')->paginate(10);
return view('user.index', ['users' => $users ]);
users.blade.php
@foreach($users as $user)
{{ $user->name }}
@endforeach

{{ $users->links() }}

これだけで、10件ごとにユーザー名が表示されるページネーションが簡単にできます。

今回のエラーの原因

Laravelでは、テーブル名を複数形、モデルを単数形にするというルールがありますが、今回私はどうしてもinfoという不可算名詞を使用したかったため、任意の名前でテーブル名を設定しました。
ですので、viewのforeachをこれまで書いていたものと同様、Laravelに頼りすぎて何も考えず下記のように書きました。

example.blade.php
@foreach($info as $info)
{{ $info->name }}
@endforeach

{{ $info->links() }}

すると先程のエラー。
変数がオーバーライドされるので、当たり前ですね...下記のように変更(例)しました。

example.blade.php
@foreach($info as $information)
{{ $information->name }}
@endforeach

{{ $info->links() }}

無事ページネーションができました。

おわりに

恥ずかしながらLaravelの便利さ故に詰まってしまいました。自分で任意の名前でテーブル名を設定できますが、できれば変なところで詰まりたくないので、Laravelではできるだけ命名規則に従っておこうと思います。

参考

https://readouble.com/laravel/6.x/ja/pagination.html

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

『アウトプット』の重要性

・記事の概要

webエンジニアとして活動したく、現在独学でPHPの学習に取り組んでいます。
PHPを用いたWebアプリケーション開発経験を積んでいきたいので、
効率的な学習のためにもインプット、アウトプット、フィードバックを繰り返していくためにこの場をかりていこうと思います。

ショッピングサイトやチケット購入サイトを利用することが多く、自分でもECサイト関連を作ってみたいと思ったので
ポートフォリオを作成の記録を残していこうと思います。

①スペック

言語:PHP 7.2.24

DBMS:MySQL

CSSフレームワーク:milligram

開発環境:macOS Catalina 10.15.6、Apache 2.4.46

バージョン管理:Git 2.21.0

本番環境:Heroku

②Webアプリケーションの機能

公演のチケットを購入することができる。

サイトマップ
IMG_6492.jpg

データベース図は後ほど。

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

PHP 0を使って桁数を揃える

目的

  • PHPのsprintf関数を使用して数値の桁数を合わせる方法をメモ的にまとめる

情報

方法

  • 下記を実行する。

    printf("%'空欄部分を埋めたい文字桁数d\n", 数値);
    
  • 例えば数値「123456789」を「00123456789」としたいときは下記のようになる。

    printf("%'011d\n", 123456789);
    

参考文献

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

PHP8にアップデートしたら、laravelでMethod ReflectionParameter::getClass() is deprecated in ... エラーが出た話

使用していたバージョン

Laravel Framework 7.30.1
PHP 8.0.0

エラー発生時

laravelでいつものようにphp artisan コマンドを使ったが、

Method ReflectionParameter::getClass() is deprecated in ...

長ったらしい文が出てきて、コマンドが使えない。

参考サイトによるとphp8で推奨されていないらしい、、、。

解決方法

  • composer.jsonファイルのphpのバージョンを変更 例えば
php:^7.2.5  =>  php:^8.0
  • コマンドでcomposer updateを実行 で解決します。

** また、バージョン変更により本番環境でがシステムが動かなくなる可能性があるので、
念のため

php:^7.2.5  =>  php:^8.0
ではなく

php:^7.2.5|^8.0

上記のように書き換えるといいです。

参考サイト

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

サーバーにruby、クライアントにPHPでgRPCを頑張る

はじめに

google謹製のRPCフレームワークであるgRPCを使って、サーバーにruby、クライアントにPHPを使って、異なる言語間を共通のI/FでAPI通信してみたいと思います。

異なる言語間としているのはgRPCのメリットであるIDL定義によるI/Fの共通化の醍醐味を味わってみたいというより、PHPでgRPCサーバの構築ができないという現実に対する条件反射のようなものですが、結果的に醍醐味もやってくるので、味わってみたいと思います。

クライアントにPHPを使う場合、サーバー側はどうもgoで紹介しているところが多いので、ここではrubyを使ってみます(はい、go知らないの確定)。

概要はここで書くまでもないので、さっそく。
ここではAPIとして、ドメインを指定すると有効期限を取ってくる、というものを書いてみます。

INPUT: FQDN
OUTPUT: yyyy/mm/dd hh:ii:ss

API(gRPC): 定義したI/Oで共通I/Fを用意
クライアント(PHP): サーバを通じてAPIにFQDNをリクエスト
サーバ(ruby): クライアントからリクエストされたFQDNからSSL証明書の有効期限を調べてAPI経由で日付をレスポンス

事前作業

pecl install grpc
pecl install protobuf
vi ()/php.ini
    extension=grpc.so
    extension=protobuf.so

gem install grpc
gem install grpc-tools

mkdir ~/projects/grpc_trial/
cd ~/projects/grpc_trial/

composer require grpc/grpc
composer require google/protobuf

mkdir {protos,php,ruby}

実作業

vi protos/checkexpires.proto
    (後述)

grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/checkexpires.proto
ls ruby/ | grep checkexpires
    checkexpires_pb.rb
    checkexpires_services_pb.rb

protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/checkexpires.proto --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
ls php/Checkexpires/
    CheckReply.php  CheckRequest.php  GetSslClient.php

vi checkexpires.rb
    (後述)

vi checkexpires.php
    (後述)

ruby ./checkexpires.rb &
php ./checkexpires.php 
php checkexpires.php example.com
    2021/12/25 23:59:59
checkexpires.proto
syntax = "proto3";

package checkexpires;

import "google/protobuf/timestamp.proto";

service GetSsl {
  rpc getExpire (CheckRequest) returns (CheckReply) {}
}

message CheckRequest {
  string fqdn = 1;
}

message CheckReply {
  google.protobuf.Timestamp timestamp = 1;
}
checkexpires.rb
this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'ruby')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'time'
require 'grpc'
require 'checkexpires_services_pb'

class CheckexpiresServer < Checkexpires::GetSsl::Service
  def get_expire(check_req, _unused_call)
    end_at = `openssl s_client -connect #{check_req.fqdn}:443 </dev/null 2>/dev/null|openssl x509 -text | grep "Not After"`
    end_at = end_at.split(' : ').pop
    end_at = end_at.gsub("\n", '')
    end_at = Time.parse(end_at).to_i
    end_at = Time.at(end_at)
    Checkexpires::CheckReply.new(timestamp: end_at)
  end
end

def main
  s = GRPC::RpcServer.new
  s.add_http2_port('localhost:50051', :this_port_is_insecure)
  s.handle(CheckexpiresServer)
  s.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT'])
end

main
checkexpires.php
<?php
require dirname(__FILE__).'/vendor/autoload.php';
require dirname(__FILE__).'/php/Checkexpires/GetSslClient.php';
require dirname(__FILE__).'/php/Checkexpires/CheckReply.php';
require dirname(__FILE__).'/php/Checkexpires/CheckRequest.php';
require dirname(__FILE__).'/php/GPBMetadata/Checkexpires.php';
$server = 'localhost:50051'; //checkexpires.rb

if (is_null($argv[1] ?? null)) {
    echo "need to input fqdn\n";
    exit(1);
}

try {
    $client = new Checkexpires\GetSslClient($server, [
        'credentials' => Grpc\ChannelCredentials::createInsecure(),
    ]);
    $request = new Checkexpires\CheckRequest();
    $request->setFqdn($argv[1]);
    list($reply, $status) = $client->getExpire($request)->wait();

    if (($status->code ?? null) === 0) {
        $ts = $reply->getTimestamp()->getSeconds();
        echo date('Y/m/d H:i:s', $ts);
        exit(0);
    }
} catch (Exception $e) {
    echo $e->getMessage();
    exit(1);
}

補足

せっかく異なる言語で共通I/Fを定義しているのですから、それぞれのコンパイラも一個に共通化してしまいましょう。

vi ~/.bashrc
    function protoc2() {
        grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/$@ && protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/$@ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
    }

. ~/.bashrc
cd ~/projects/grpc_trial/
protoc2 checkexpires.proto

これで、ひとつのprotoファイルでrubyとPHPそれぞれの共通I/Fが準備できました。
あとはクライアント側とサーバ側にそれぞれ固有な処理を書いていけばいい感じですね。
頑張れそうです。

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

Eloquentのall()ってなんなの? いきなりget()しちゃダメなの?

はじめに

LaravelでDBからデータを取り出す処理を書いているときに、大体all()ってやるじゃないですか。

$hoges = Hoge::all()

で、自分で書いておきながら「あれ? 全件取り出しの時all()って書いたのなにげに初めてじゃね?」って思ったんですね。実際、他のところではいきなりget()してました。

$fugas = Fuga::get();

で、この2つ、同じ動きをするわけなんですが、何が違うんじゃ? と思った次第です。

all() のコードを見てみよう

公式ドキュメント見ても何がどう違うのか書いてはいなかったので、コードを見てみることにしました。

vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(454-465)
    /**
     * Get all of the models from the database.
     *
     * @param  array|mixed  $columns
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    public static function all($columns = ['*'])
    {
        return static::query()->get(
            is_array($columns) ? $columns : func_get_args()
        );
    }

どうも、普通にget()を呼んでいるだけのようです。
ただ、違うところは取得するカラムを引数で指定できるようになっているのですが、配列、もしくは可変長で取れる様になっているところみたいです。

つまり、

$hoges = Hoge::all(['id', 'updated_at']);
$hoges = Hoge::all('id', 'updated_at');

は同じ動きをするってことですね。
get()の場合は引数が1つの配列、もしくは文字列しか取れないので

動かない例
$hoges = Hoge::get('id', 'updated_at');

これは動きません。

結論

  • いきなり get() でも良さそう
  • 全件取得してるよ!ってのを明示的にしたければ all() を使う感じかな?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テスト投稿

テスト投稿

エンジニアを目指して

これからがんばります!

phpのインストール時のエラー

Error: The following formula cannot be installed from bottle and must be
built from source.
  python@3.9
Install the Command Line Tools:
  xcode-select --install

と出た。xcodeってなんだっけと思いながら

xcode-select --install

を実行するもエラー。
そういえばPCの容量一杯になったときにアプリ消したか?覚えてないけどとにかくxcodeを入れようとするも容量不足で入れられず。

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

XAMPPでPHP8からPHP7へバージョンを下げる

XAMPPでPHP8からPHP7へバージョンを下げる

使用環境

  • XAMPP 8.0 (PHP 8.0)
  • windows10

背景

PHP7で開発していたLaravelアプリを別のPCで(PHP8)でも開こうと思ったら開けない!composer installがエラーになった。どうやらPHPのバージョンが違うぞ!と怒られたっぽい。ということでPHPをダウングレードすることにした。

XAMPPのApacheは消しておく

起動したまま操作しない方が良いみたい。

PHP7をダウンロードする

公式ページからPHP7をダウンロードする。今回、使用したのはVC15 x64 Thread Safe (2021-Jan-05 18:10:24)というファイル。
PHPダウンロード

XAMPP内のPHPディレクトリをバックアップする

コピーでもOKなのですが、私はディレクトリ名をPHP → PHP8に変更した。

ダウンロードしたPHP7のディレクトリをPHPディレクトリとしてXAMPPディレクトリ内に移動

PHPディレクトリを新しく作って、その中に解凍したPHP7関連のファイルを全て放り込む。

PHP8で使用していたphp.iniをPHP7(PHPディレクトリ)にコピー

コピーしてそのまま使用。

ApacheのPHP設定を編集する

C:\xampp\apache\conf\extra\httpd-xampp.conf 内のPHP8と書かれているところを全てPHP7に変換。

Apacheを起動

と思ったら、Apacheが起動しない!!!!
シャットダウン時に予期せぬエラーが発生した...みたいなことが書いてある!?
嫌な予感。MySQLが起動しないパターンでXAMPPを再インストールした過去があるのですごく嫌な予感。

image.png

PHPのバージョン確認してみる

コマンドプロンプトでphp -v を入力してみる。
エラーが発生。なんかファイルが無いっぽいことをいってる。

PHP Warning:  Cannot open 'C:\xampp\php\extras\browscap.ini' for reading in Unkno
wn on line 0

Warning: Cannot open 'C:\xampp\php\extras\browscap.ini' for reading in Unknown on
 line 0
PHP Fatal error:  Unable to start standard module in Unknown on line 0

Fatal error: Unable to start standard module in Unknown on line 0

よくわからないけどPHP8のディレクトリに
xampp\php\extras\browscap.ini があったのでコピーしてPHP7のxampp\php\extrasに置いてみた。

もう一度バージョン確認

XAMPPのshellを起動してphp -v と入力すると、きちんとPHP8からPHP7にバージョンが変わっている。

さぁ、Apacheの起動だ

と思ったら、再度エラー。起動しない。

image.png

調べていくとコンパイラとか違ったら設定ファイルとか使いまわせないみたいなことが出てきた。
さらに調べてみるとどうやらSAPIモジュールというものが変更されているらしい。

Apache2Handler

PHP モジュールの名前が php7_module から php_module に変更されました。
PHP公式ドキュメント

ということで
xampp\apache\conf\extra\http-xampp.conf 内のphp_moduleと書かれているところをphp7_moduleに変更。全部で4か所あった。

正直、どれが作用しているのかはわからない。

3度目の正直、Apacheの起動

今回は無事、起動できた。

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

xdebug version 3になって動かなくなった

はじめに

新年になったので、開発環境のDockerをビルドし直したら、xdebugでのステップ実行ができなくなった。
調べてみると、xdebugのVersion3になって設定項目名が変更されてしまったため、以前の設定が無効になってしまったのが原因でした。

TL;DR

以下の設定で動きました。
ちなみにデバッガ(IDE=phpstorm, vscode)の方の待受ポートは9003に合わせる必要があります。

xdebug.log=/tmp/xdebug.log
xdebug.mode=develop, debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003

zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20170718/xdebug.so

v2時代の設定

たぶん、こんな設定がインターネットで検索するとでてくると思います。こちらはversion2までの設定になります。

xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_port=9003
xdebug.remote_host="host.docker.internal"
xdebug.remote_connect_back = 0
xdebug.remote_log=/tmp/xdebug.log

zend_extension=xdebug.so

xdebugのバージョン確認方法

php -vとするとwith Xdebug v3.0.2という風に表示されます。

root@57b67435355c:/# php -v
Cannot load Xdebug - it was already loaded
PHP 7.2.33 (cli) (built: Sep 10 2020 15:18:34) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Xdebug v3.0.2, Copyright (c) 2002-2021, by Derick Rethans

動作確認(phpstorm)

phpstormを使っているので、xdebugの動作確認になります。
以下の画像中の矢印で示しているデバッグポートですが、もともと9000, 90003と書いてありましたが、③の検証を実行したところ、9003にするように指示があったので変更しています。

2021-01-07_10h40_09.png

画像の中では、xdebug.modeがdebugじゃないと警告がでていますが、公式ドキュメントには、以下のように複数設定が有効なようです。実際、ステップ実行は実現できました。

2021-01-07_10h44_56.png

参考文献

公式サイト一択じゃないかな。。。。

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

初心者がLaravelでアプリをつくるまで

プログラミングスクールに通い始めて2ヶ月、、
あと1ヶ月で予定の3ヶ月を終えてしまうけど、全然ポートフォリオに使う予定のアプリが完成しない、、
とりあえず、全然連絡の取れなかったメンターの方を変更していただいてようやく方向性が定まってきた。

とりあえず、つくってみるアプリは前々から少し考えていた、アンケートを投稿できるモノにした。
あまり難しそうなものを考えると心が折れそうなので、、

内容としては、登録してくれたユーザが誰でもアンケートを投稿できて、他のユーザに回答してもらえて回答の集計を取れるようなものにしようかと、、
最初はとりあえず型だけつくって、後から時間があれば色々な機能を搭載していければいいかなってくらいです。

まず言われた通り、基本設計とテーブル定義を調べながらやってみたけど、やり方が全然わからないのでとりあえずシートに書いてみました。

機能一覧↓
018496678e2a5cd8a337d937404349fd.png

画面設計↓
08422cc56b25641aa748709dade904c9.png

a1aeef6548ca6759de2f7c4f4f2dc235.png

b184dce9016d9ccd1124620aa0e10e4a.png

url一覧↓
9f3a7cb6144517f2a1ea8bdd927e4c30.png

きっと間違いと足りない部分が多すぎると思うので、明日のメンタリング教えてもらおう、、

コードをこれから書いていくのですが、何から書き始めていいかさっぱりなのでスクールのカリキュラムを真似しながら1つずつ書いて行きます、、

頭の中では、このページのこの部分を押すとここに飛んで、あのボタンを押すとこうなってと、イメージはあるのですが、、
それを、紙にペンで書いていいなら今日中に終わるのに、コードでそれを表現するには初心者の自分からすれば莫大な時間がかかりそうです、、

Qiita書いてて気が付くともう朝方なので、また明日からがんばります!

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

PHPのSJIS系文字コードを表にしてみた

はじめに

※ 本記事ではShift_JISを拡張した文字コードをSJIS系文字コードと記載しています。

Shift_JIS、Shift-JIS、SJIS、SJIS-win、CP932、MS932、Windows-31J、MS_Kanjiなどなど、、、
SJIS系文字コードってたくさんありますよね。
「Shift_JISでcsv下さい」って言われたからShift_JISに変換したら「髙」が〜「ハイフン」が〜と言われたことある方は少なくないと思います。

サポートされる文字エンコーディングに記載されている中でよく使用するSJIS系の文字コードは、SJIS、SJIS-win、CP932の3種類だと思います。

サポートされるエンコーディングの概要のShift_JISの説明と注記に「IANA の定義によると、Shift_JIS のコードセットは IBM932 / CP932 とは微妙に異なります。」「CP932 コードマップを使用するには、代わりに SJIS-WIN を使用してください。」と記載されているため今までこのように考えていたんですが、実際は少し違うようです。

エンコーディング名 間違った解釈
SJIS Shift_JIS
CP932 Windows-31J?
SJIS-win NEC拡張のCP932?

しかもサポートされる文字エンコーディングに記載されていない文字コードでも実際には指定することができます。
下記は全てError、Warning、Noticeが出ることなく変換できます。

<?php
mb_convert_encoding('テスト', 'SJIS');
mb_convert_encoding('テスト', 'SHIFT-JIS');
mb_convert_encoding('テスト', 'Shift_JIS');
mb_convert_encoding('テスト', 'CP932');
mb_convert_encoding('テスト', 'MS932');
mb_convert_encoding('テスト', 'MS_Kanji');
mb_convert_encoding('テスト', 'Windows-31J');
mb_convert_encoding('テスト', 'SJIS-win');

どれが同じでどれが違う文字コードなのか、、、
このモヤモヤを解消するため、ソースコードを読んで表にまとめてみました。

エンコーディングとエイリアスの表

mime_nameがShift_JISで登録されている文字コードを全て羅列しています。

エンコーディング名 エイリアス
SJIS x-sjis, SHIFT-JIS
CP932 MS932, Windows-31J, MS_Kanji
SJIS-win SJIS-open, SJIS-ms
SJIS-2004 SJIS2004, Shift_JIS-2004
SJIS-mac MacJapanese, x-Mac-Japanese
SJIS-Mobile#DOCOMO SJIS-DOCOMO, shift_jis-imode, x-sjis-emoji-docomo
SJIS-Mobile#KDDI SJIS-KDDI, shift_jis-kddi, x-sjis-emoji-kddi
SJIS-Mobile#SOFTBANK SJIS-SOFTBANK, shift_jis-softbank, x-sjis-emoji-softbank

エイリアスにShift_JISが登録されている文字コードはなかったんですが、実際にファイル出力してみたところSJISと同じっぽいです。
UTF-8環境で実行しているためか、CP932とSJIS-winで文字化けする文字に違いはありませんでした。

$file = fopen('test.txt', 'w');
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SJIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SHIFT-JIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'Shift_JIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'CP932')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'MS932')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SJIS-win')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'Windows-31J')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'MS_Kanji')); #=> 髙閒ー--㌢㏍?
fclose($file);

まとめ

UTF-8からの変換であれば、SJIS or CP932の2択で良さそうです。
Shift_JISのエイリアスであるはずのMS_Kanjiが、PHPではCP932のエイリアスとして登録されているのは不思議な感じがしました。

結局SJIS-winが何を指しているのかはよくわかりませんでした。
そのうち「依存する文字集合」を確認して追記しようと思います。

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

PHPのSJIS系文字エンコーディングとエイリアスを表にまとめてみた

はじめに

※ 本記事ではShift_JISを拡張した文字エンコーディングをSJIS系文字エンコーディングと記載しています。

Shift_JIS、Shift-JIS、SJIS、SJIS-win、CP932、MS932、Windows-31J、MS_Kanjiなどなど、、、
SJIS系文字エンコーディングってたくさんありますよね。
「Shift_JISでcsv下さい」って言われたからShift_JISに変換したら「髙」が〜「ハイフン」が〜と言われたことある方は少なくないと思います。

サポートされる文字エンコーディングに記載されている中でよく使用するSJIS系文字エンコーディングは、SJIS、SJIS-win、CP932の3種類だと思います。

サポートされるエンコーディングの概要のShift_JISの説明と注記に「IANA の定義によると、Shift_JIS のコードセットは IBM932 / CP932 とは微妙に異なります。」「CP932 コードマップを使用するには、代わりに SJIS-WIN を使用してください。」と記載されているため今までこのように考えていたんですが、実際は少し違うようです。

エンコーディング名 間違った解釈
SJIS Shift_JIS
CP932 Windows-31J?
SJIS-win NEC拡張のCP932?

しかもサポートされる文字エンコーディングに記載されていない文字エンコーディングでも実際には指定することができます。
下記は全てError、Warning、Noticeが出ることなく変換できます。

<?php
mb_convert_encoding('テスト', 'SJIS');
mb_convert_encoding('テスト', 'SHIFT-JIS');
mb_convert_encoding('テスト', 'Shift_JIS');
mb_convert_encoding('テスト', 'CP932');
mb_convert_encoding('テスト', 'MS932');
mb_convert_encoding('テスト', 'MS_Kanji');
mb_convert_encoding('テスト', 'Windows-31J');
mb_convert_encoding('テスト', 'SJIS-win');

どれが同じでどれが違う文字エンコーディングなのか、、、
このモヤモヤを解消するため、ソースコードを読んで表にまとめてみました。

エンコーディングとエイリアスの表

mime_nameがShift_JISで登録されている文字エンコーディングを全て羅列しています。

エンコーディング名 エイリアス
SJIS x-sjis, SHIFT-JIS
CP932 MS932, Windows-31J, MS_Kanji
SJIS-win SJIS-open, SJIS-ms
SJIS-2004 SJIS2004, Shift_JIS-2004
SJIS-mac MacJapanese, x-Mac-Japanese
SJIS-Mobile#DOCOMO SJIS-DOCOMO, shift_jis-imode, x-sjis-emoji-docomo
SJIS-Mobile#KDDI SJIS-KDDI, shift_jis-kddi, x-sjis-emoji-kddi
SJIS-Mobile#SOFTBANK SJIS-SOFTBANK, shift_jis-softbank, x-sjis-emoji-softbank

エイリアスにShift_JISが登録されている文字エンコーディングはなかったんですが、実際にファイル出力してみたところSJISと同じっぽいです。
UTF-8環境で実行しているためか、CP932とSJIS-winで文字化けする文字に違いはありませんでした。

$file = fopen('test.txt', 'w');
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SJIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SHIFT-JIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'Shift_JIS')); #=> ??ー--???
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'CP932')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'MS932')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'SJIS-win')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'Windows-31J')); #=> 髙閒ー--㌢㏍?
fwrite($file, mb_convert_encoding("髙閒ー−-㌢㏍㍿\n", 'MS_Kanji')); #=> 髙閒ー--㌢㏍?
fclose($file);

まとめ

UTF-8からの変換であれば、SJIS or CP932の2択で良さそうです。
Shift_JISのエイリアスであるはずのMS_Kanjiが、PHPではCP932のエイリアスとして登録されているのは不思議な感じがしました。

結局SJIS-winが何を指しているのかはよくわかりませんでした。
そのうち「依存する文字集合」を確認して追記しようと思います。

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

PHPの??ってなに?

まだ現場経験が浅いときに思ったこと

「??ってなに?」

現場経験3か月頃、初めて下記の処理を見たとき、一体どういう処理なのか?と
悩んだ覚えがあるので、同じような方向けに記事にしました。

$hoge = $a ?? null

下記のような三項演算子なら知っていましたが、「??」はあまり馴染みない方も多いはず。。

$a = 1;
$b = 2;

$max = $a > $b ? $a : $b;
echo $max;
実行結果
2

結論

$hoge = $a ?? null

これは下記の処理と同じことをしています。
aに値があればaの値を代入し、無ければnullが入るようなイメージです。

if ($a) {
    $hoge = $a;
} else {
    $hoge = null;
}

以上、短い記事でしたがどなたかの参考になれば幸いです。

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

【API初心者】無料APIを使って簡単な飲食店検索アプリを作ろう

アプリ概要

 ぐるなびAPIを利用した店舗検索が可能なWEBアプリケーションです。日本語に加え、英語、中国語(簡体字)、韓国語、ベトナム語の計5ヶ国語に対応しています。また、スマホで利用するユーザが多くなるだろうと想定して、レスポンシブデザインに対応しています。誰でも簡単に条件指定して店舗検索できることが、このアプリの最大の特徴であり長所だと言えます。

背景

 2021年1月現在、世界全体でコロナウイルスによる大変な被害に見舞われています。このような状況が一刻も早く治り、以前よりも多くの人が日本に訪れることを夢見ています。実現するかどうかはわかりませんが、世界からやってくる人たちに、少しでも良いので、日本の飲食店の凄さを知ってもらいたいと願っています。どこで食べようかと場所を決める際に、簡単に飲食店を検索できたら良いのではないか、と思いついたのが、このアプリを作ろうと思った背景になります。

準備

 ぐるなびWebサービスにて、新規アカウントを発行し、keyidを取得します。keyidは各種APIに送信するリクエストパラメータとして必須なので、忘れないように注意しましょう。

仕様

 選択フォーム画面1、選択フォーム画面2、検索結果表示画面の3画面構成となっています。選択フォーム画面1および選択フォーム画面2にて、店舗の検索条件を指定し、検索結果の店舗一覧を検索結果表示画面に表示する簡単な仕様になっています。また、各画面において、上部のリンクから指定の言語リンクを押下することで、表示が対象の言語に切り替わる仕様になっています。

選択フォーム画面1

 この画面においては、飲食店のカテゴリーおよび都道府県を選択します。カテゴリーの選択肢一覧は、keyid、対象言語をリクエストパラメータに指定し、ぐるなびの大業態マスタ取得APIから取得したレスポンスを加工して表示しています。都道府県の選択肢一覧は、同じくkeyid、対象言語をリクエストパラメータに指定し、都道府県マスタAPIから取得したレスポンスを加工して表示しています。「次へ」ボタンを押下すると、選択フォーム画面2に遷移するのですが、その際に大業態コード、都道府県コード、対象言語をGETで送信します。

スクリーンショット 2021-01-06 23.19.36.png

選択フォーム画面2

 この画面においては、選択フォーム画面1で選択した都道府県に紐づく、詳細な地域を選択します。地域の選択肢一覧は、keyid、GETで取得した都道府県コードおよび対象言語をリクエストパラメータに指定し、エリアSマスタ取得APIから取得したレスポンスを加工して表示しています。「戻る」ボタンを押下すると、選択フォーム画面1に戻ります。「送信」ボタンを押下すると、検索結果表示画面に遷移するのですが、その際に大業態コード、エリアSコード、都道府県コード、対象言語をGETで送信します。

スクリーンショット 2021-01-06 23.18.29.png

検索結果表示画面

 この画面においては、検索条件に応じた店舗一覧を表示します。店舗一覧は、keyid、GETで取得した大業態コード、エリアSコード、都道府県コード、対象言語をリクエストパラメータに指定し、多言語版レストラン検索APIから取得したレスポンスを加工して表示しています。

スクリーンショット 2021-01-07 0.15.48.png

完成形

URL
GitHub

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