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

PHP用の日本語ランダムジェネレータを作りました

PHPのランダムジェネレータはfzaninotto/Fakerは有名です。

名前などの日本語バージョンもありますが、単語、住所、メールなど色々欲しい物がなかったので、fzaninotto/Fakerを利用して、作りました。

インストール

コンポーザー利用、インストール

composer require xyyo/faker-japanese

使い方(ほぼ全ての例)

  • 元々日本語特定用のFaker\Factory::create('ja_JP')は不要
  • 全てのロジックは自分のプロバイダーに

以下の例をご覧ください。

<?php
// psr-4 クラスのオートロード
require_once 'vendor/autoload.php';

$faker = Faker\Factory::create();

$faker->addProvider(new Faker\Provider\Japanese($faker));

// カスタマイズの項目
// テキスト関連
var_dump($faker->kanji); // 純
var_dump($faker->word); // 基礎
var_dump($faker->idiom); // 公明正大
var_dump($faker->pokemon); // ゼニガメ
var_dump($faker->bank); // 宮崎銀行
var_dump($faker->color); // 深緑
var_dump($faker->company); // 株式会社小泉輸送用機械器具

// インターネット関連
var_dump($faker->url); // www.akemi.jp
var_dump($faker->email); // matsumoto@yahoo.co.jp
var_dump($faker->userFirstName); // akemi
var_dump($faker->userLastName); // ishida
var_dump($faker->userName); // kyosuke sasaki

// 元々のコードベースにある、移行した項目
// アドレス関連
var_dump($faker->postcode); // 491-1292
var_dump($faker->country); // ブータン
var_dump($faker->prefecture); // 滋賀県
var_dump($faker->ward); // 西区
var_dump($faker->city); // 中村市
var_dump($faker->streetAddress); // 大垣町9-1-6
var_dump($faker->secondaryAddress); // 小林コーポ105号
var_dump($faker->address); // 茨城県笹田市西区江古田町10-6-6宇野コーポ110号
var_dump($faker->addressWithCode); // 484-5349  熊本県青山市西区江古田町4-7-8 西之園ハイツ105号

// 名前関連
var_dump($faker->lastName); // 坂本
var_dump($faker->firstName('male')); // 拓真
var_dump($faker->firstName('female')); // 陽子
var_dump($faker->name('male')); // 宮沢 智也
var_dump($faker->name('female')); // 西之園 結衣
var_dump($faker->lastKanaName); // カノウ
var_dump($faker->firstKanaName('male')); // マナブ
var_dump($faker->firstKanaName('female')); // カナ
var_dump($faker->kanaName('male')); // ヤマダ ヒロシ
var_dump($faker->kanaName('female')); // タナカ アケミ

var_dump($faker->phone); // 014-204-8044
var_dump($faker->domainName); // yamada.info
var_dump($faker->realText); // このぼんやりと白い銀河ぎんがを大きないい望遠鏡ぼうえんきょうで見ますと、もうたくさんの小さな星に見えるのです。

to do

  • fzaninotto/Fakerに PR を出す。
  • Laravel の使い方の例を出す。

他のパッケージじゃなくて、やっぱり本家を使いたい気持ちもあるので、時間があれば、fzaninotto/FakerにPRを出したい。
その前に、簡単なLaravelのfactorytest例も追加したい。


もしおかしい所や他のランダムジェネレータに追加して欲しいがあれば、教えていただければと思います。
少しでも役に立たら、githubの⭐️やコメントは更に嬉しいです。

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

日本語専用のPHPランダムジェネレータを作りました

  • PHPのランダムジェネレータはfzaninotto/Fakerは有名だと思いますが、名前などの日本語バージョンもあります。
  • でも、単語、住所、メールなども色々欲しい!
  • 無かったので、fzaninotto/Fakerを利用して、作りました。

➡︎ Github⛩

インストール

コンポーザー利用、インストール

composer require xyyo/faker-japanese

使い方(ほぼ全ての例)

  • 元々日本語特定用のFaker\Factory::create('ja_JP')は不要
  • 全てのロジックは自分のプロバイダーに

以下の例をご覧ください。

<?php
// psr-4 クラスのオートロード
require_once 'vendor/autoload.php';

$faker = Faker\Factory::create();

$faker->addProvider(new Faker\Provider\Japanese($faker));

// テキスト関連
$faker->color => 薄橙
$faker->kanji => 
$faker->word => 給料
$faker->idiom => 画竜点睛
$faker->pokemon => スリーパー
$faker->realText => 大きな望遠鏡ぼうえんきょうで銀河ぎんがをよっく調しらべると銀河ぎんがはだいたい何でしょう

// インターネット関連
$faker->domainName => saito.org
$faker->url => www.hiroshi.com
$faker->email => hirokawa@yahoo.co.jp
$faker->userFirstName => osamu
$faker->userLastName => yamada
$faker->userName => yamaguchi chiyo

// アドレス関連
$faker->bank => 北九州銀行
$faker->postcode => 869-5565
$faker->country => 合衆国領有小離島
$faker->prefecture => 三重県
$faker->ward => 西区
$faker->city => 松本市
$faker->streetAddress => 西之園町3-7-3
$faker->secondaryAddress => 吉田コーポ104号
$faker->address => 佐賀県山口市南区中村町4-5-9
$faker->addressWithCode => 304-6851  奈良県田中市南区佐藤町5-4-3 若松ハイツ105号

// 人間関連
$faker->phone => 00332-8-0234
$faker->company => 有限会社佐々木木材
$faker->lastName => 渡辺
$faker->firstName('male') => 太一
$faker->firstName('female') => 明美
$faker->name('male') => 松本 涼平
$faker->name('female') => 村山 春香
$faker->lastKanaName => ツダ
$faker->firstKanaName('male') => ヨウスケ
$faker->firstKanaName('female') => モモコ
$faker->kanaName('male') => ナカジマ ミツル
$faker->kanaName('female') => ナカムラ ハルカ

Laravel の Factory の使い方

// UserFactory.php
$factory->define(User::class, function (Faker $faker) {
    // add this simple line here
    $faker->addProvider(new \Faker\Provider\Japanese($faker));
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
.....

tinker ですぐ確認しましょう !
Psy Shell v0.9.12 (PHP 7.3.13  cli) by Justin Hileman
>>> factory(App\User::class,2)->create()
=> Illuminate\Database\Eloquent\Collection {#3330
     all: [
       App\User {#3326
         name: "宮沢 直人",
         email: "satomi.xidao@example.org",
         email_verified_at: "2020-02-17 12:21:38",
         updated_at: "2020-02-17 12:21:38",
         created_at: "2020-02-17 12:21:38",
         id: 5,
       },
       App\User {#3335
         name: "大垣 智也",
         email: "tsubasa08@example.org",
         email_verified_at: "2020-02-17 12:21:38",
         updated_at: "2020-02-17 12:21:38",
         created_at: "2020-02-17 12:21:38",
         id: 7,
       },
     ],
   }

to do

  • fzaninotto/Fakerに PR を出す。

他のパッケージじゃなくて、やっぱり本家を使いたい気持ちもあるので、時間があれば、fzaninotto/FakerにPRを出したい。


少しでも役に立たら、githubの⭐️やコメントは嬉しいです。

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

Herokuにデプロイしたら画像が突然取得できなくなった

Herokuにデプロイした直後に画像が消える

開発環境:cloud9
言語:PHP7.2
フレームワーク:Laravel 5.5

ローカルサーバーでLaravelのプロジェクトを起動していた時はアプリ内で画像は問題なく表示されていました。ちなみにこの時から画像はAWSのS3に保存して取得するようにしていました。アプリの最低限の機能ができたのでherokuへデプロイしてみましたが、画像が一切読み込めなくなりました。おまけに、表示されるエラーが"something went wrong."だけなので困りました。

Herokuのログを確認

とりあえず不具合の原因を探るためにhrokuのログを確認してみました。

$ heroku config:set APP_LOG=errorlog
$ heroku logs

ログを見ると、S3の関連でエラーが出ていることが分かりました。

HerokuにS3用の環境設定をしていなかった

初心者あるあるかもしれませんが、非常に初歩的なミスで、HerokuのconfigファイルにS3の設定を書き込んでいませんでした。というわけで、configファイルにS3の設定を書き込みました。

heroku config:set AWS_ACCESS_KEY_ID=自分のアクセスキーID
heroku config:set AWS_SECRET_ACCESS_KEY=S3へのシークレットアクセスキー
heroku config:set S3_BUCKET_NAME=S3バケットの名前
heroku config:set AWS_DEFAULT_REGION=AWSのリージョン(バージニア北部ならus-east-1)

これで問題なく画像が表示されるようになりました。

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

マジックメソッドのPhpDocを自動生成する方法

はじめに

Laravelにはide-helper(php artisan ide-helper:models)というphpdocを自動生成してくれる便利なライブラリがありますが、ide-helperの対応範囲外のクラスにおいてもこれと同じようなことができないかということを考えてみました。

実現したこと

クラス内のプロパティに関するgetメソッドのphpdocを自動生成いたしました。
このことによりgetterメソッドを毎回書く必要がなくなり、getのマジックメソッドを基底クラスに一つ書いておくことでコード全体をすっきりとさせることができます。

仕組み

PHPにはクラス情報を取得するReflectionClassという関数があります。
この関数を使うとなんと!PhpDocの情報やプロパティの情報も取得できたりします。
つまり既存PhpDocの内容に対し、プロパティのgetメソッドを追加することもできます。
※ide-helperでもこれと似たような手法でPhpDocの自動生成を行っています

サンプルコード

/**
 * Class UpdateGetterMethodPhpDoc
 */
class UpdateGetterMethodPhpDoc
{
    /**
     * 指定ファイル内のPhpDocを更新する(プロパティのGetメソッドを追加)
     *
     * @param array $file_paths ファイルパス一覧
     * @param string $filtering_class_name 対象となるクラス名
     */
    public function run(array $file_paths, string $filtering_class_name)
    {
        foreach ($file_paths as $file_path) {
            // 対象となるクラス以外は何もしない
            if (strpos($file_path->getRealPath(), $filtering_class_name) === false) {
                continue;
            }

            // ファイル名からクラスを生成し、該当クラス内のプロパティのGetメソッドをPhpDocとして定義する
            foreach (ClassMapGenerator::createMap($file_path->getRealPath()) as $model => $class_file_path) {
                $php_docs = [];
                $reflection = new ReflectionClass($model);

                if (empty($reflection->getDocComment())) {
                    throw new LogicException('クラスのPhoDocが未定義です。必ず入力して下さい');
                }

                foreach ($reflection->getProperties() as $property) {
                    preg_match('/@var([^\n]*)/', $property->getDocComment(), $variable_data_list);
                    $this->verifyProperty($reflection->getName(), $property, $variable_data_list);
                    [
                        $data_type,
                        $description,
                    ] = explode(' ', ltrim($variable_data_list[1]));
                    $php_docs[] = sprintf('%s %s() %s', $data_type, Str::camel($property->getName()), $description);
                }

                $blade_file = view('Component.PhpDoc.base', [
                    'namespace'  => $reflection->getNamespaceName(),
                    'class_name' => $reflection->getShortName(),
                    'php_docs'   => $php_docs,
                ])->render();

                $new_file = str_replace($reflection->getDocComment(), rtrim($blade_file), File::get($class_file_path));
                File::delete($class_file_path);
                File::put($class_file_path, $new_file);
            }
        }
    }

    /**
     * プロパティが正しく定義されているかどうかの検証
     *
     * @param string $class_name クラス名
     * @param ReflectionProperty $property プロパティ情報
     * @param string[] $variable_data_list プロパティーの@var部分を切り取った情報 [0:キーワード(@var)を含む文字列、1:マッチした部分のみの文字列]
     */
    private function verifyProperty(string $class_name, ReflectionProperty $property, array $variable_data_list)
    {
        if (empty($property->getDocComment())) {
            throw new LogicException(sprintf('%sのプロパティである%sのPhoDocが未定義です。必ず入力して下さい', $class_name, $property->getName()));
        }

        if (empty($variable_data_list) || empty($variable_data_list[1])) {
            throw new LogicException(sprintf('%sのプロパティである%sのPhoDocが正しくありません。@var及び型を付けて下さい', $class_name, $property->getName()));
        }

        if (strpos($variable_data_list[1], 'mixed') !== false) {
            throw new LogicException(sprintf('%sのプロパティである%sのPhoDocが正しくありません。プロパティの型にmixedは使用しないで下さい', $class_name, $property->getName()));
        }
    }
}

/**
 * Class PhpDocCreateCommand
 */
class PhpDocCreateCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'php_doc_create {target_directory} {filtering_class_name}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'プロパティ用のGetメソッドのためのphpdocを生成する';

    /**
     * プロパティ用のGetメソッドのためのphpdocを生成する
     */
    public function handle()
    {
        $filtering_class_name = $this->argument('filtering_class_name');
        $file_paths = File::allFiles(app_path($this->argument('target_directory')));

        $update_getter_method_php_doc = new UpdateGetterMethodPhpDoc();
        $update_getter_method_php_doc->run($file_paths, $filtering_class_name . '.php');
    }
}
base.blade.php
/**
 * Class {{ $class_name }}
 *
 * @package{{ sprintf(' %s',$namespace) }}
@foreach($php_docs as $php_doc)
 * @@method {{ $php_doc }}
@endforeach
 */
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PhpDocが書かれているかどうかをチェックする方法

はじめに

PhpDoc皆さん書いてますか?
補完を最大限効かせるためにできれば全部書いていきたいですが、抜けているかどうかのチェックを目視で確認するのって面倒ですよね。
面倒なことは自動化しちゃいましょう!

仕組み

PHPにはクラス情報を取得するReflectionClassという関数があります。
そして、この関数を使うとなんと!PhpDocの情報も取得できたりします。
つまり、書いてなければnullが返ってきます。
※補足すると各メソッドの行番号やプロパティの内容、publicかどうかなども取得できます。

サンプルコード

/**
 * Class PhpDocCheckCommand
 */
class PhpDocCheckCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'php_doc_check {target_directory_path}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'phpdocの存在をチェックする';

    /**
     * phpdocの存在をチェックする
     */
    public function handle()
    {
        $target_directory_path = app_path($this->argument('target_directory_path'));
        $check_php_doc = new CheckPhpDoc();
        $check_php_doc->run($target_directory_path);
    }
}

/**
 * Class CheckPhpDoc
 */
class CheckPhpDoc
{
    /**
     * 指定ファイル内のPhpDocが正しく書かれているかどうかを検証する
     *
     * @param string $target_directory_path 対象のディレクトリのパス
     */
    public function run(string $target_directory_path)
    {
        foreach (ClassMapGenerator::createMap($target_directory_path) as $model => $class_file_path) {
            $this->verifyPhpDoc($model);
        }
    }

    /**
     * クラスのPhpDocの検証
     *
     * @param $model
     */
    private function verifyPhpDoc($model)
    {
        $reflection = new ReflectionClass($model);

        // クラスのPhpDocの検証
        if (empty($reflection->getDocComment())) {
            throw new LogicException(sprintf('%sクラスのPhoDocが未定義です。必ず入力して下さい', $reflection->getName()));
        }

        // クラス内のプロパティのPhpDocの検証
        foreach ($reflection->getProperties() as $property) {
            if (empty($property->getDocComment())) {
                throw new LogicException(sprintf('%sのプロパティである%sのPhoDocが未定義です。必ず入力して下さい', $reflection->getName(), $property->getName()));
            }
        }

        // クラス内のメソッドのPhpDocの検証
        foreach ($reflection->getMethods() as $method) {
            if (empty($method->getDocComment())) {
                throw new LogicException(sprintf('%sのメソッドである%sのPhoDocが未定義です。必ず入力して下さい', $reflection->getName(), $method->getName()));
            }
        }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カスタム投稿タイプの作り方

カスタム投稿を作成するとき、何かと忘れてしまうのでメモ。✍️

functions.php
add_action( 'init', 'create_post_type' );
function create_post_type() {
    register_post_type( 'news', [ // 投稿タイプ名の定義
        'labels' => [
            'name'          => 'プロダクト', // 管理画面上で表示する投稿タイプの名前
            'singular_name' => 'Product',    // 管理画面上で表示する投稿タイプの名前(単数)
        ],
        'menu_icon'     => 'dashicons-admin-page', // 管理画面上で名前の左に表示するアイコン
        'public'        => true,  // 投稿タイプをpublicにするか
        'show_in_rest'  => true,  // 新エディタ「Gutenberg」を有効にする
    ]);
}

functions.phpに上記を書いた際、以下のように表示される。

image.png


register_post_typeの引数の詳細はcodexに書いてあります。
関数リファレンス/register_post_type

管理画面上で名前の左に表示するアイコンの名前はORGから調べることができます。
Developer Resources

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

PHP の Rest で Cloud Firestore のデータを削除 (Delete)

firestore_delete_rest.php
#! /usr/bin/php
<?php
//
//  firestore_delete_rest.php
//
//                  Feb/16/2020
//
// ------------------------------------------------------------------
include('Requests/library/Requests.php');
include("get_token.php");
//
// ------------------------------------------------------------------

// ------------------------------------------------------------------
fputs (STDERR,"*** 開始 ***\n");
Requests::register_autoloader();
//
$key_in = $argv[1];
print($key_in . "\n");

$token = get_token_proc();
//
$project = 'project-jan25-2020';
$headers = array('Authorization' => 'Bearer ' . $token);
$options = array();
$url_base = "https://firestore.googleapis.com/v1/projects/" . $project . "/databases/(default)/documents/cities";
$url = $url_base . "/" . $key_in;

$request = Requests::delete($url, $headers);
var_dump($request->status_code);

$rvalue = $request->body;

print($rvalue);

fputs (STDERR,"*** 終了 ***\n");
// ------------------------------------------------------------------
?>

get_token.py はこちら
PHP の Rest で Cloud Firestore のデータを作成 (Create)

実行コマンド

export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_delete_rest.php t0922
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP の Rest で Cloud Firestore のデータを更新 (Update)

firestore_update_rest.php
#! /usr/bin/php
<?php
//
//  firestore_update_rest.php
//
//                  Feb/16/2020
//
// ------------------------------------------------------------------
include('Requests/library/Requests.php');
include("get_token.php");
//
// ------------------------------------------------------------------
fputs (STDERR,"*** 開始 ***\n");
Requests::register_autoloader();
//
$key_in = $argv[1];
$population_in = intval ($argv[2]);
print($key_in . "\n");
print($population_in . "\n");

$token = get_token_proc();
//
$project = 'project-jan25-2020';
$headers = array('Authorization' => 'Bearer ' . $token,
    'Content-Type' => 'application/json');
$options = array();
$url_base = "https://firestore.googleapis.com/v1/projects/" . $project . "/databases/(default)/documents/cities";
$url = $url_base . "/" . $key_in;
$url .= "?updateMask.fieldPaths=population&updateMask.fieldPaths=date_mod";

date_default_timezone_set('Asia/Tokyo');
$today = date ("c");
print($today . "\n");

$update01 = array ();
$update01["fields"] = array ();
$update01["fields"]["population"] = ["integerValue"=> $population_in];
$update01["fields"]["date_mod"] = [ "timestampValue"=> $today];

$str_json = json_encode ($update01);
print($str_json . "\n");

$request = Requests::patch($url, $headers, $str_json);
var_dump($request->status_code);

$rvalue = $request->body;

print($rvalue);

fputs (STDERR,"*** 終了 ***\n");
// ------------------------------------------------------------------
?>

get_token.php はこちら
PHP の Rest で Cloud Firestore のデータを作成 (Create)

実行コマンド

export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_update_rest.pho t0923 91476200
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP の Rest で Cloud Firestore のデータを読む (Read)

firestore_read_rest.php
#! /usr/bin/php
<?php
//
//  firestore_read_rest.php
//
//                  Feb/03/2020
//
// ------------------------------------------------------------------
include('Requests/library/Requests.php');
include("get_token.php");
//
// ------------------------------------------------------------------

// ------------------------------------------------------------------
fputs (STDERR,"*** 開始 ***\n");
Requests::register_autoloader();
//
$token = get_token_proc();
// print( $token );
//
$project = 'project-jan25-2020';
$headers = array('Authorization' => 'Bearer ' . $token);
$options = array();
$url = "https://firestore.googleapis.com/v1/projects/" . $project . "/databases/(default)/documents/cities";

$request = Requests::get($url, $headers);
var_dump($request->status_code);
// var_dump($request->body);

$json_string = $request->body;
$dict_aa = json_decode ($json_string,true);
// var_dump($dict_aa["documents"]);

$array_aa = $dict_aa["documents"];
foreach ($array_aa as $unit_aa)
    {
    $str_aa = $unit_aa["name"];
    $tt = explode("/",$str_aa);
    $length = count($tt);
    $key = $tt[$length - 1];
    $name = $unit_aa["fields"]["name"]["stringValue"];
    $population = $unit_aa["fields"]["population"]["integerValue"];
    $date_mod = $unit_aa["fields"]["date_mod"]["timestampValue"];
//
    $str_out = $key . "\t" . $name . "\t" . $population . "\t" . $date_mod;
    print($str_out . "\n");
    }

fputs (STDERR,"*** 終了 ***\n");
// ------------------------------------------------------------------
?>

get_token.php はこちら
PHP の Rest で Cloud Firestore のデータを作成 (Create)

実行コマンド

export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_read_rest.php
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP の Rest で Cloud Firestore のデータを作成 (Create)

firestore_create_rest.php
#! /usr/bin/php
<?php
//
//  firestore_create_rest.php
//
//                  Feb/16/2020
//
// ------------------------------------------------------------------
include('Requests/library/Requests.php');
include("get_token.php");
//
// ------------------------------------------------------------------
function add_data_proc($url_base,$headers,$key,$name,$population,$date_mod)
{
$url = $url_base . "?documentId=" . $key;

$update01 = array ();
$update01["fields"] = array ();
$update01["fields"]["name"] = ["stringValue"=> $name];
$update01["fields"]["population"] = ["integerValue"=> $population];
$update01["fields"]["date_mod"] = [ "timestampValue"=> $date_mod];

$str_json = json_encode ($update01);
print($str_json . "\n");
$request = Requests::post($url, $headers, $str_json);
var_dump($request->status_code);

$rvalue = $request->body;

print($rvalue);
}

// ------------------------------------------------------------------
fputs (STDERR,"*** 開始 ***\n");
Requests::register_autoloader();
//

$token = get_token_proc();
//
$project = 'project-jan25-2020';
$headers = array('Authorization' => 'Bearer ' . $token,
    'Content-Type' => 'application/json');
$options = array();
$url_base = "https://firestore.googleapis.com/v1/projects/" . $project . "/databases/(default)/documents/cities";


add_data_proc($url_base,$headers,'t0921','宇都宮',38921,"2010-10-5T00:15:00Z");
add_data_proc($url_base,$headers,'t0922','小山',45921,"2010-10-9T00:14:00Z");
add_data_proc($url_base,$headers,'t0923','佐野',76921,"2010-11-7T00:16:00Z");
add_data_proc($url_base,$headers,'t0924','足利',94921,"2010-12-15T00:12:00Z");
add_data_proc($url_base,$headers,'t0925','日光',27921,"2010-8-2T00:11:00Z");

fputs (STDERR,"*** 終了 ***\n");
// ------------------------------------------------------------------
?>
get_token.php
<?php
//
//  get_token.php
//
//                  Feb/03/2020
//
// ------------------------------------------------------------------
function get_token_proc ()
{
    $token = '';
    $command = 'gcloud auth application-default print-access-token';

    try
        {
        exec($command, $out, $ret);
        $token = $out[0];
        }
    catch (Exception $ee)
        {
        print("*** error *** exec ***\n");
        print('Error:'.$ee->getMessage());
        }

    return  $token;
}

// ------------------------------------------------------------------
?>

実行コマンド

export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_create_rest.php
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Moodle 3.8 マニュアル - frankenstyle

原文

Frankenstyle

'Frankenstyle コンポーネント名' は、Moodle プラグインをプラグインとその名前で識別するための命名規則です。Moodle コード全体で(顕著な例外はテーマの css クラス名です)使用されています。

Martin Dougiamas が Petr Škoda が発明した 'frankenstyle' というを単語を命名システムとして記述することを発明しました。

内容

1 フォーマット
2 プラグインタイプ
3 コアサブシステム
4 使用方法
4.1 関数名
4.2 クラス名
4.3 定数
4.4 テーブル名
4.5 プラグイン設定テーブル
4.6 ケーパビリティ
4.7 言語ファイル
4.8 レンダラー
4.9 モジュールサブプラグイン
4.10 その他の場所(TODO)
5 テーマ命名の変形
6 関連項目

1 フォーマット

Frankenstyle コンポーネント名は接頭辞とアンダースコアで分離されたフォルダ名を持ちます。

  • 接頭辞はプラグインのタイプ(翻訳準備中)により決定されます。例えば activity モジュールの接頭辞は mod です。
  • 名前はプラグインのフォルダ名で常に小文字です。たとえば、Quiz の名前は quiz です。

したがって、quiz モジュールの frankenstyle コンポーネント名は mod_quiz となります。

2 プラグインタイプ

See Plugin types page for the list of all supported plugin types in Moodle and their frankenstyle prefix.

To get a definitive list in your version of Moodle 2.x, use a small Moodle script with print_object(get_plugin_types());.

3 コアサブシステム

Subsystems in Moodle are not plugins themselves but can be referred to using core_xxx where xxx is the subsystem name as defined in get_core_subsystems().

You can see these being used in the @package parameter in phpdocs, or in the webservice function names. Core subsystems can provide own strings via a file stored in lang/en/{subsystemname}.php. Some of them have a dedicated location with libraries, autoloaded classes and other resources.

コアサブシステム Frankenstyle コンポーネント名 場所
Access core_access
Administration core_admin /admin
Antivirus core_antivirus /lib/antivirus
Authentication core_auth /auth
Conditional availability core_availability /availability
Backup and restore core_backup /backup/util/ui
Badges core_badges /badges
Blocks core_block /blocks
Blogging core_blog /blog
Bulk users operations core_bulkusers
Caching core_cache /cache
Calendar core_calendar /calendar
Cohorts core_cohort /cohort
Comment core_comment /comment
Competency based education core_competency /competency
Completion core_completion /completion
Countries core_countries
Course core_course /course
Currencies core_currencies
Database transfer core_dbtransfer
Debugging core_debug
Text editors core_editor /lib/editor
Education fields core_edufields
Enrol core_enrol /enrol
Error reporting core_error
Favourites core_favourites /favourites
File picker core_filepicker
Files management core_files /files
User filtering core_filters
Forms core_form /lib/form
Grades core_grades /grade
Advanced grading core_grading /grade/grading
Groups core_group /group
Help core_help
Hub core_hub
IMS CC core_imscc
Installer core_install
ISO 6392 core_iso6392
Language pack configuration core_langconfig
License core_license
Maths library core_mathslib
Media core_media
Messaging core_message /message
MIME types core_mimetypes
MNet core_mnet /mnet
Dashboard core_my /my
User notes core_notes /notes
Page types core_pagetype
Pictures and icons core_pix
Plagiarism core_plagiarism /plagiarism
Plugins management core_plugin
Portfolio core_portfolio /portfolio
Privacy core_privacy /privacy
Course publishing core_publish /course/publish
Question bank engine core_question /question
Ratings core_rating /rating
Site registration core_register /admin/registration
Repository core_repository /repository
RSS core_rss /rss
Roles core_role /admin/roles
Global search core_search /search
Tabular data display/download (deprecated) core_table
Tagging core_tag /tag
Timezones core_timezones
User core_user /user
User key core_userkey
Web service core_webservice /webservice

4 使用方法

Frankenstyle component names are used in:

4.1 関数名

All plugin functions must start with full frankenstyle prefix. For backwards compatibility modules may also use modulename_ as prefix.

4.2 クラス名

All plugin classes must start frankenstyle prefix, ex: mod_forum_some_class.

4.3 定数

All plugin constants must start with uppercase frankenstyle prefix, example MOD_FORUM_XXXX.

4.4 テーブル名

All table names for a plugin must begin with its frankenstyle name (after the standard Moodle table prefix).

(The exception to this rule is Moodle activities which (for historical reasons) do not have mod_ in front of the plugin name)

Examples: mdl_local_coolreport, mdl_local_coolreport_users

4.5 プラグイン設定テーブル

In the config_plugins table, column plugin, the frankenstyle name is used.

4.6 ケーパビリテイ

All capabilities for a plugin use the frankenstyle name, except with a / instead of a _.

Example: mod/quiz:viewattempt

4.7 言語ファイル

The main language file for each plugin (with the notable exception of activity modules) is the frankenstyle component name.

Examples: /blocks/participants/lang/en/block_participants.php

4.8 レンダラー

4.9 モジュールサブプラグイン

It is possible to extend modules without having to change the basic module's code. See Subplugins for details.

4.10 その他の場所(TODO)

  • @package declarations in phpdocs, see Coding_style#PHPDoc
  • web service function names
  • Moodle Plugins database

Please add more as they come up.

5 テーマ命名の変形

Themes that are principally derivatives of some other theme, should be named in a way that clearly implies they are variants, not upgrades, of the parent theme. The format of the name should follow and extend the standard component naming format. So for example themes based on Boost, if they want to mention the word "boost" in the component name, should be named like

theme_boost_something

for example theme_boost_spring, theme_boost_summer, theme_boost_shiny etc. Indeed they can be named just like

theme_something

as there is no need to repeat the parent name (e.g. theme_spring, theme_summer, theme_shiny). The point and reasoning behind this is to avoid confusion - for example a theme_boost2 looks like a new version of the official Boost theme and as such is not a good name for a contributed theme.

6 関連項目

  • Plugins
  • Subplugins
  • Core APIs
  • Automatic class loading

カテゴリ:プラグイン

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

Moodle 3.8 マニュアル - カテゴリ:プラグイン

Pages in category "Plugin"

The following 261 pages are in this category, out of 261 total.

2

2D/3D structure display short answer

3

360-degree feedback module

A

Adaptable theme
admin/setting/atto styles settings
admin/setting/blocksettingslideshow
admin/setting/themesettingaigne
Algebra question type
All or nothing multiple choice question type
Assignment files report
Attendance activity
Atto Word import
auth/dev
Availability dataformcontent
Azuresearch

B

Benchmark report
BigBlueButtonBN
BIM module
block eledia coursewizard
Blocks/Add-on/GISMO/overview
blocks/catalogue
Boost Campus theme
BTEC marking
Bulk enrolments
Buttons course format

C

Calculated Objects question type
Campus theme
Capability explorer
Certificate module
Checklist module
Cloud Poodll Assignment Feedback
Cloud Poodll Assignment Submission
Cloud Poodll for Atto
Cloud Poodll for TinyMCE
Cloud Poodll Recording Question
Cloze editor for Atto
Cloze editor for TinyMCE
CodeRunner question type
Cohort external database
Collabora Activity Module
Collapsed Topics course format
Combined question type
Community hubs
Completion Progress block
Concept map question type
Configurable reports
Correct writing question type
Course Archiving
Course checks block
Course Menu block
Course overview (legacy)
Course search
CreditEnrolment
Custom certificate module
Custom Navigation

D

Daily course format
Dataform entry access block
Dataform field access block
Dataform module
Dataform notification block
Dataform view access block
Dataform view block
Dataform view module
Datalynx module
Delete User by Maillist
Dialogue module
Differentiator local plugin
DocRank
Download courses
Download users by role
Drag and drop matching question type
Drag and drop organic chemistry nomenclature question type

E

Earth theme
Elasticsearch
Electron pushing / curved question type
eLeDia Webservicesuite
English Central
Essay (auto-grade) question type
Essential theme
Event reminders
Evolve-D theme
Exabis e-portfolio block
Exabis games activity

F

Face-to-face module
Faces block
filter generico
filter multiembed
FirstClass authentication
Fischer projections question type
Flashcard module
Flexible sections course format
format glendon
Forum Keyword report
Freehand drawing question type

G

Game module
Gapfill question type
GeoGebra
Gift with medias format
Glossary export to quiz
Google Apps Integration
Grader report LAE
Grid course format
Group choice activity
Group choice quick guide
Group self-selection module

H

Heatmap block
Hot Question
Hotpot module
HTML per role block

I

iAssign
iAssign Filter
IMAP authentication
Inactive User Cleanup
Insert interactive activity (iAssign) by TinyMCE editor
Inspire
Interactive Content - H5P activity
IPA filter

J

Javaunittest question type
Journal module
Jupiter theme

K

Key signature question type
Kickstart course format
Kprime question type

L

LAEGrader report
Learning Analytics Enriched Rubric
Level up!
Lewis structures
Lightbox Gallery
Login as
Luna theme

M

Magtest module
Manager Interactive Activities of iAssign
Mars theme
Mathslate for Atto
Matrix question type
Mercury theme
Message My Teacher Block
Message provider
MH AAIRS block
MindMap Course block
mod enrolmenttimer
mod/ltisource ecohub
Moderator guide
Moodle app additional features
Moodle glossary entries format
Moodle Mobile availability plugin
Moodle Mobile quiz offline attempts
Moodle Version Block
MoodleBox
MoodleDT - Development Tools Plugins for Moodle
Moossenger block
Morecandy
Multi-lingual Content filter
Multinumerical question type
Multiple True/False Question Type
Multitopic course format
Music interval question type
Music theory question type
Musical scale question type
My stats block
MyMobile theme

N

Name to structure or reaction question type
Neptune theme
Newman projections question type
NNTP authentication

O

Office365
Offline quiz activity
Opaque question type
Ordering question type
OU multiple response question type
ownCloud Repository

P

PAM (Pluggable Authentication Modules)
Pattern-match question type
Pattern-match with JME question type
PDF Annotation activity
Plugin skeleton generator
plugins/atto wiris
plugins/filter wiris
plugins/tinymce tiny mce wiris
Pluto theme
POAS abstract question type
Poll block
Poodll Assignment Feedback
Poodll Assignment Submission
PoodLL filter
Poodll Filter
Poodll for Atto
Poodll for TinyMCE
Poodll Read Aloud
Poodll Recording Question
PoodLL recording question type
PoodLL repository
Poodll Subtitle
POP3 server authentication
Portfolio module
Preg question type
Profile switches
Progress Bar block

Q

qformat qtex
qformat/csv
Question Creation module
Question practice module
question/type/jme
Talk:Questionnaire module
QUESTOURnament module
Quick Course List Block
Quick Find List Block
QuizPort module
Quizventure activity

R

Ranking block
Ratingallocate
RecordRTC
Reengagement activity
Registration module
Regular Expression Short-Answer question type
RememberMe
report/myfeedback/install
resetpassword
Reverse pattern match question type
Rocket theme

S

Saturn theme
Scheduler module
Scheduler: Booking form
Select atoms or molecules question type
Sharing Cart
Simple certificate module
Simple Clock block
softcourse
Solution sheet
Sookooroo block
Sookooroo module
Splitset question type
STACK question type
Stamp collection module
Stash block
Structure match with editor question type
StudentQuiz module
suspend user

T

TinyMCE Mathslate
Toggle preview
tool reset mymoodle
tool untoken oauth2

U

UIkit theme
Unanswered Discussions block
UNEDTrivial
Unilabel module
Uniquelogin authentication
UpgradeDB
Upload Tutor Relationships Block
Uranus theme
User bookmarks block

V

Variable numeric question type
Variable numeric set question type
Variable numeric set with units question type
Venus theme
Videofile
VideoTime
Virtual lab filter
Vitero module

W

WIRIS math & science questions
Word table format
Wordselect question type

メインページ

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

ジェネレータで大量データをCSVにファイル分割して書き込む

みんな大好きジェネレータ。クラスとか定義せずにジェネレータだけで綺麗に書けるぞ。

背景

もともとは仕事で書いたコード。先日開催されたPHPerKaigi 2020ジェネレータの事例紹介として出したので、ここで見せます。

要件

  • データベースから値を取り出してCSVにダンプしたい
    • レコード数 = とにかくいっぱい
    • 全レコードを1クエリで全部取得するのはメモリ量としてもDBの負荷としても許容できない
  • CSVは表計算ソフトで読み込ませる必要がある
    • 表計算ソフトという括りではExcelに限らず65535行以上のCSVを扱えるのは稀なので、ファイルには65534レコードごとにファイルを分割する。

実装

おいらは3重ループとか4重ループとか多重ネストさせるのは絶対に嫌なのでジェネレータで処理を分割しました。

#!/usr/bin/env php
<?php

/**
 * 実行すると記事一覧を CSV として吐き出すスクリプト
 */

// 無限ループしないように最大値を明示
$limit = 100000;
$base_dir = __DIR__ . '/csv/';

// DBコネクション: 今回は使わないのでダミー値
$con = null;

/**
 * データベースからとってきた値を無限に吐き出すよ
 *
 * @return \Generator<array>
 */
$articles_generator = function (int $step) use ($con, $limit) {
    while (true) {
        $min = $min ?? 0;
        $max = $min + $step;

        // $min から $max までの区間のレコードを全取得
        $articles = fetch_db($con, $min, $max);

        foreach ($articles as $article) {
            yield $article['id'] => $article;
        }

        // デバッグ出力は STDERR に書き込む
        fwrite(STDERR, "{$article['id']}\n");

        if ($max >= $limit) {
            return;
        }

        $min += $step;
    }
};

/**
 * 65534 レコードごとに別のCSVファイルに書き込むジェネレータ
 */
$csv_generator = function ($header) use ($base_dir) {
    while (true) {
        $i = $i ?? 0;
        $i++;

        $path = $base_dir . "records-{$i}.csv";

        echo $path, PHP_EOL;
        $fp = fopen($path, 'w');

        if ($fp === false) {
            throw new LogicException("Open failure: {$path}");
        }

        // CSV ヘッダ行
        fputcsv($fp, $header);

        // ヘッダ行を抜いて 65534 レコード書き込む
        foreach (range(2, 65535) as $_) {
            $row = yield;

            fputcsv($fp, $row);
        }

        fclose($fp);
    }
};

// ジェネレータ初期化時にCSVのファイルヘッダ(カラム)を注入
$csv = $csv_generator(['id', 'name']);

// 1回のDBリクエストで1000レコードづつ処理する
foreach ($articles_generator(1000) as $id => $article) {
    // CSV 書き込み
    $csv->send([
        $article['id'],
        $article['name'],
    ]);
}

/**
 * ダミーのDB取得関数
 *
 * @return array<array{id:int,name:string}>
 */
function fetch_db($con, int $min, int $max): array
{
    return array_map(
        function ($id) { return ['id' => $id, 'name' => "dummy value {$id}"]; },
        range($min, $max - 1)
    );
}

雑感

  • 雑にwhile (true)とか書いてるけど、ちゃんと停止します
  • $articles_generatorではDBから値を取り出すことだけに専念します
  • $csv_generatorはファイルに値を書き込むことだけに専念します
    • ジェネレータはこのように反復して呼び出される処理にも実はぴったりフィットします
    • 継続は力なり。継続万歳!
  • この実装だとユニットテストを書くにはちょっとだけ抽象化が足りてない感ありますね
    • 特にファイルに書き込むあたり
    • 動作確認できれば十分な書き捨てのスクリプトならこんなもんかな、と…
  • 無限ループって怖くね?
    • だって同じことが繰り返されるんだぜ?
      • それはどんなコードでもそう
      • 無限ループに落ちないように終了条件は十分検討が必要
  • 書き捨てじゃないコードならクラスで定義すればいいのでは?
    • それはそうかもしれない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

標準入力でCSVをPHPに渡し配列にする

やること

csvをshellスクリプトで標準入力し、phpファイルで加工する

コード

<?php
while ($row = fgetcsv(STDIN)) {
    var_dump($row);
}
$ php [ファイル名].php < [ファイル名].csv

これでcsvを成形しやすくなります

まとめ

1つのファイルを加工するのであればとても便利でした。
2つ以上のファイルを加工するのであればまた違う方法が必要なのかなと思いました。
またコマンドラインで渡すことができたら使い回しが簡単になったのでshellも便利だなと感じました。

参考

PHPでコマンドラインの入力をする
PHPで標準入力を取得する方法
PHPでCSVを配列にする

追記

@tadsan さん
コメントでの指摘ありがとうございます。
より簡単なコードを書けて勉強になりました。

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

無料でPHP使ってWebAPIをつくってみた

はじめに

以前つくった文字列→塩基配列の相互変換ツールをつくってみた(アプリ版)のWeb版(PHP)がこれにより不要になってしまいました:scream:

せっかく作ったPHP版も何かに使えないかと思いAPI化してみました:clap:

*注意 httpです:see_no_evil:

成果物

ドキュメント

BluePrintでAPIドキュメントみたいなのつくってみましたので詳細はこれを見てください:bow:

http://adventam10.php.xdomain.jp/dna/api/

ソース

下記の dna-converter.php です。

https://github.com/adventam10/DNAConverter-web/tree/master/api

API化

PHPで簡単なWebAPIを実装してみるを参考に実装!!

こんな感じで実装して下記のように XFREE にアップ!!!

無料でできるPHPのWEBサイト公開(テスト用)

<?php
header('Content-Type: text/html; charset=UTF-8');

$json_string = file_get_contents('php://input');
$json = json_decode($json_string, true);
$resultCode = checkResultCode($json);
if($resultCode === 0) {
    $arr["resultCode"] = $resultCode;
    $arr["convertedText"] = convert($json["text"], $json["mode"]);
} else {
    $arr["resultCode"] = $resultCode;
}

print json_encode($arr, JSON_PRETTY_PRINT);

swift で確認してみる

let urlString = "http://adventam10.php.xdomain.jp/dna/api/dna-converter.php"
var request = URLRequest(url: URL(string: urlString)!)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = try? JSONEncoder().encode(Request(mode: 0, text: "あいうえお"))
let task = URLSession.shared.dataTask(with: request) { (data, _, error) in
  let response = try? JSONDecoder().decode(Response.self, from: data!)
  print(response)
}
task.resume()

struct Request: Codable {
    let mode: Int
    let text: String
}

struct Response: Codable {
    let resultCode: Int
    let convertedText: String
}

ちゃんと以下のように取得できていました:clap:

Optional(Test.Response(resultCode: 0, convertedText: "GCAGCAATCAACGCAGCAATCATAGCAGCAATCATCGCAGCAATCACAGCAGCAATCACC"))

ハマったとこ

POSTMAN を使って確認していたのですが下記のようにリクエストをしていると mode が数値ではなく文字列になってしまいハマりました。

postman_1

その時のPHPの実装が下記

$resultCode = checkResultCode($_POST);
function checkResultCode($param) {
  if (isset($param["mode"])) {
    $mode = $param["mode"];
    if ($mode === 0 || $mode === 1) { // 文字列なのでここでアウト
      if (!empty($param['text'])) {
        $text = $param['text'];
        if ($mode === 0) {
          return 0;
        } else {
          if (isInvalidDNA($text)) {
            return 4;
          } else {
            return 0;
          }
        }
      } else {
        return 3;
      }
    } else {
      return 2;
    }
  } else {
    return 1;
  }
}

POSTMAN で下記のように JSON を設定すると数値が送れるらしいと聞いたので修正。

postman_2

が、しかし、PHP が $_POST だったので値を受け取れず:scream:

どうやら $_POST では JSON を受け取れない模様。

参考:JSON形式でPOSTされたデータの受信方法

下記のように PHP を修正。

$json_string = file_get_contents('php://input');
$json = json_decode($json_string, true); // 第2引数にtrueを設定しないとダメらしい
$resultCode = checkResultCode($json);
function checkResultCode($param) {
  if (isset($param["mode"])) {
    $mode = $param["mode"];
    if ($mode === 0 || $mode === 1) {
      if (!empty($param['text'])) {
        $text = $param['text'];
        if ($mode === 0) {
          return 0;
        } else {
          if (isInvalidDNA($text)) {
            return 4;
          } else {
            return 0;
          }
        }
      } else {
        return 3;
      }
    } else {
      return 2;
    }
  } else {
    return 1;
  }
}

無事数値を取得できました:clap:

ドキュメント作成

せっかくなんで API Blueprint でドキュメントを作成してみました。

参考:API BlueprintでWeb APIのドキュメントを生成する

ハマったとこ

node.js を https://nodejs.org/ja/ ここから取得して入れていたので npm install -g aglio がパーミッションエラーになってしまいました:scream:

下記参考に設定を行い無事インストールできました:clap:

npmでpermission deniedになった時の対処法[mac]

ドキュメントの書き方あんまわからなかったので下記参考にしながらそれっぽいものを書いてみました。

API Blueprint を使って Web API の仕様書を書こう

さいごに

とりあえずこれで PHP で作成したものも無駄にならずにすみました:tada:

これでどこからでもDNA変換ができます!!!

(swift の POST がライブラリ使わずにやるのやり方忘れてて地味に苦労しました:speak_no_evil:

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