20200218のPHPに関する記事は11件です。

Docker上でPHP拡張モジュール『GD』を有効化する

この記事ではDocker上でPHP拡張モジュール『GD』を有効化する方法をお伝えします。

どうしてGDの有効化が必要になったか

LaravelとVueを用いたSPAを勉強したく、『Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう』に取り組んでいました。

(9)写真投稿APIでテストを実行した際に
Error: Call to undefined function Illuminate\Http\Testing\imagecreatetruecolor().
というエラーで行き詰まり、原因を調査したところPHP拡張モジュール『GD』の有効化が必要との情報にたどり着きました。

環境

version
PHP 7.4.1
Laravel 6.15.0
Docker for Mac 19.03.5

GD有効化の方法

DockerファイルにGDの有効化、必要なライブラリのインストールの部分を記載することでGDを有効化できます。
有効化した後、再ビルドすることで正しく動作するようになります。

まずはGDが使えない状態のDockerfileをご紹介します。

Dockerfile
FROM php:7.4.1-fpm

COPY install-composer.sh /
RUN apt-get update \
  && apt-get install -y wget git unzip libpq-dev \
  && : 'Install Node.js' \
  &&  curl -sL https://deb.nodesource.com/setup_12.x | bash - \
  && apt-get install -y nodejs \
  && : 'Install PHP Extensions' \
  && docker-php-ext-install -j$(nproc) pdo_pgsql \
  && : 'Install Composer' \
  && chmod 755 /install-composer.sh \
  && /install-composer.sh \
  && mv composer.phar /usr/local/bin/composer

WORKDIR /var/www/html/vuesplash

【参考】
Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (3) SPA開発環境とVue Router

これを下記のように変更します。

:Dockerfile
FROM php:7.4.1-fpm

COPY install-composer.sh /
RUN apt-get update \
  && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
  && : 'Install Node.js' \
  && curl -sL https://deb.nodesource.com/setup_12.x | bash - \
  && apt-get install -y nodejs \
  && : 'Install PHP Extensions' \
  && docker-php-ext-install -j$(nproc) pdo_pgsql \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install -j$(nproc) gd \
  && : 'Install Composer' \
  && chmod 755 /install-composer.sh \
  && /install-composer.sh \
  && mv composer.phar /usr/local/bin/composer

WORKDIR /var/www/html/vuesplash

変更点は3行です。


まずは5行目。

-  && apt-get install -y wget git unzip libpq-dev \
+  && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \

libfreetype6-devlibfreetyep6-devlibjpeg62-turbo-devlibpng-devの4つのライブラリを追加しています。

【参考】
docs/php at master · docker-library/docs


2点目の変更点は、変更後のファイル11行目に新たに一行挿入しています。

+  && docker-php-ext-configure gd --with-freetype --with-jpeg \

PHP7.4系ではdocker-php-ext-configureの引数が変更されているので注意が必要です。

【参考】
【PHP】Docker PHP7.4系でgdをインストールしてimagecreatefromjpegを使う
docs/php at master · docker-library/docs


最後に変更後のファイルの12行目のように、GDのインストールコマンドを追加して変更完了です。

+  && docker-php-ext-install -j$(nproc) gd \

【参考】
docs/php at master · docker-library/docs

まとめ

Laravelを勉強しているものの、基本的な言語仕様は同じだろうと高をくくっており、PHP自体の構成や拡張モジュールについては全く勉強しておらず、長時間ハマり続けてしまいました…

これを機に基本的な言語仕様や拡張モジュールについても勉強していければと思います。

また、長時間ハマり続けた別の理由として、古い情報がうまくいかない場合が多かったこともあるので、今回の記事のように苦労して解決したことは積極的に記事にしていきたいです。

まだまだPHPもLaravelもDockerも未熟なので、誤りがありましたらご指摘いただけると幸いです。

参考

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

【基本文法編】Ruby/Python/PHPの違い

はじめに

個人的な学習のまとめです!
3つの言語の基本文法を学習したのでまとめてみました!

1. 出力

Ruby
   puts "Hello, world"
Python
   print("Hello, world")
PHP
   echo "Hello, world";

2. 変数

Ruby
   variable = "変数”
Python
   variable = "変数”
PHP
  $variable = "変数”;

3.プロパティ値の出力

Ruby
   # attr_accessor :name 等でゲッターを定義する
   puts person.name
Python
   # def __init__ 等でゲッターを定義する
   print(person.name)
PHP
  echo $person->name();

4. If文

Ruby
   if number % 15 == 0
     puts "FizzBuzz" 
   elsif number % 3 == 0
     puts "Fizz"
   elsif number % 5 == 0
     puts "Buzz"
   else 
     puts number
   end  
Python
   if number % 15 == 0:
     print("FizzBuzz") 
   elif number % 3 == 0:
     print("Fizz")
   elif number % 5 == 0:
     print("Buzz")
   else:
     print(number)  
PHP
  if (number % 15 == 0){
     echo "FizzBuzz";
   } elseif (number % 3 == 0){
     echo "Fizz";
   } elseif (number % 5 == 0){
     echo "Buzz";
   } else {
     echo $number;
   }

5. 配列/ハッシュ編

Ruby
   # 配列
   array = ["赤", "青", "黄"]
   # ハッシュ
   hash = {"大阪府": "大阪", "愛知県": "名古屋", "東京都": "東京?"}
Python
   # リスト
   lists = ["赤", "青", "黄"]
   # 辞書
   dict = {"大阪府": "大阪", "愛知県": "名古屋", "東京都": "東京?"}
PHP
    //配列
   $array = ["赤", "青", "黄"];
   //連想配列
   $associative_array = [
           "大阪府" => "大阪",
           "愛知県" => "名古屋",
           "東京都" => "東京?"
   ];

6. 繰り返し構文

6-1.ループ処理

Ruby
   i = 1
   while i <= 100 do
     # 処理
     i += 1
   end
Python
   i = 1
   while i <= 100:
    # 処理
    i += 1

   for i in range(1, 101):
    # 処理
PHP
  for ($i = 1;$i <= 100;$i++){
  //処理 
  }

6-2. 反復処理(?)

Ruby
   array.each do |value|
     #処理
   end

   hash.each {|key, value|
    #処理
   }
Python
  for value in lists:
    # 処理

  for key in dict:
     # 処理
PHP
  foreach($array as $value){
    //処理 
  }

  foreach($associativeArray as $key => $value){
    //処理
  }

7.関数定義

Ruby
  def hello
   # 処理
  end   
Python
   def hello():
     # 処理
     return # 戻り値
PHP
  function hello() {
    // 処理
    return // 戻り値
  }

8.クラスとインスタンス

Ruby
   class Person
     @@number = 0

     def self.classmethod
      # 処理
     end

     def initialize(name)
        @name = name
     end

     person = Person.new("山田")
   end   
Python
  class Person:
     number = 0

     @classmethod
     def classmethod(cls):
       # 処理

     def __init__(self, name):
       self.name = name
     end

     person = Person("山田")
   end   
PHP
  class Person {
    private static $number;
    private $name;

    public function static function classmethod(){
      echo self::$number;
    }

    public function __construct($name){
      $this->name = $name;
    }
    $person = new Person();
  }

9.ゲッターとセッター

Ruby
   def setName(name)
     @name = name
   end

   def getName
    @name
   end
   #あるいは
   attr_accessor :name
Python
   def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name
PHP
  public function setName($name) {
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

10.最後に

1ブロックをどのように(end/インデント/{})指定するかに個性がある感じですね!
ご一読頂き、ありがとうございました!:relaxed:

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

instagram graph api 登録のちょっとした手順

案件でPHPの投稿を取得し、サイト内に表示させる機会がありました。
珍しくないことではあるが、実案件としては初めての機会であり、取得→表示に手こずったので(説明をきちんと読まない悪い癖のせいでもあるが)、忘れないようにメモしておきます。

API

この類のAPIは以下が挙げられるかと思います。
https://web.analogstd.com/tips/posts/api/instagram-grapgh-api-facebook.php

Instagram API

提供元:Instagram

言わずもがなですが、2020年に利用停止となりますので、今から使う方はいないでしょう。。。
現状、使用しているサイトは見直しが必要ですね

Instagram Graph API

提供元:Facebook
今回こちらを採用しました。

トークンの取得

利用には、トークンの取得が必要です。今回、この手順はディレクターにおまかせして、諸々登録ご完了後に必要そうな情報を一式いただきました。
なので、今回は説明を割愛します。また、機会があれば改めてまとめることにしよう。
すごいめんどくさいらしいです。。。

最終的に、必要となる情報は

  • 無期限のトークン(場合によって、第3のトークンと呼ばれる。)
  • Instagramのビジネスアカウント(Facebookビジネスより登録。)

すで無期限のトークンを頂いていましたが、そもそもそれが何なのかわからなかったので、見直しがてら自分で作業を行いました。

無期限のトークンの取得

取得したトークン2を使って下記のurlにアクセスします。
https://graph.facebook.com/v3.0/me?access_token=【トークン2】
すると、オブジェクトが表示されるはずです。

{
 "name":"hoge",
 "id":"fuga"
}

トークン2IDを使用して、下記にアクセスします。
https://graph.facebook.com/v4.0/【ID】/accounts?access_token=【トークン2】
(※vはバージョンです。適宜変更してください。自分はv4.0だったはず)
以下のように表示されたらおkです。

{
"data":[
{
 "access_token":"fugafuga",
 "category":"",

// 省略
}

このaccess_token第三のトークン(無期限のトークン)と呼ばれる最終必要情報です!(ここまでが長い〜)
表示されたアクセストークンを、facebook for developerのアクセストークンデバッカーに入力して、諸々設定を確認
とりあえず、有効期限は受け取らないにしておくといいかと思います。
(この辺の作業、ディレクターに行っていただいたので詳細は割愛させてください。。。。)

投稿の確認

ここまで間違っていないか確認します。
https://graph.facebook.com/【アクセストークンデバッガーで確認できるページID】/feed?fields=permalink_url,id,created_time,message,from,full_picture,attachments{unshimmed_url}&access_token=【無期限のトークン】

問題なければ、instagramの投稿情報がもろもろ表示されるはずです。

{
 "data": [
  {
   "permalink_url": "https://facebookurl",
   "id": "000",
   "created_time": "2019-",
   "message": "hogehoge", 
  // 略

perma_linkにアクセスして、問題なく該当の投稿が表示されればおkです。

instagram ビジネスアカウント

無限のトークンの他に必要な情報です。下記、アクセスします。
https://graph.facebook.com/【アクセストークンデバッガーで確認できるページID】?fields=instagram_business_account&access_token=【無期限のトークン】
以下のように表示されたらおkです。

"instagram_business_account": {
 "id": "1234567890"
},
"id": "1234567890"

出力

いよいよ出力します。
今回は、jsonとしてデータを受け取り、jsで表示処理を別途行います。(ajaxでとりあえずconsole出力してみましょう。)
こんな感じでしょうか。
(セキュリティの問題上、jsで処理するのではなく、PHPで処理しましょう。)

get.php
$accessToken = 'hogehoge'; // アクセストークン
$businessId = '111111'; // ビジネスアカウントID
$fields = 'media{caption,media_url,permalink,timestamp}'; // 取得するデータのパラメータ
$url = 'https://graph.facebook.com/v4.0/'.$businessId."?fields=name%2Cmedia.limit(4)%7Bcaption%2Clike_count%2Cmedia_url%2Cpermalink%2Ctimestamp%2Cthumbnail_url%2Cmedia_type%2Cusername%7D&access_token=".$accessToken;

$jsonData = file_get_contents($url);
$json = json_decode($jsonData, true);
echo json_encode($json["media"]["data"]);

最後に

記事の書き始めから、2週間ほど経過しており少しすでに曖昧になっている点も有りました。(冒頭、忘れないためのメモ書きという趣旨を書いていましたが。。。)間違いがありましたら、申し訳ありません。

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

PHPを使ってPDFを作成する

今回使うライブラリは、既存のPDF文書をテンプレートとして引用できるので、静的なレイアウト作成や細かい調整などはwordやExcelで行うことができます。
動的なデータに関しては、座標指定で配置可能です:grinning:

ライブラリとフォントをダウンロードする

  • TCPDF
    PDFを作成して書き込みができるライブラリ
  • FPDI
    既存のPDFを読み込めるライブラリ
  • 明朝フォント

それぞれ解凍して、任意の場所に配置してください。
(あとでパスを指定すればいいのでどこでもOK)

実際のコード

write_pdf.php
// 文字コードをUTF-8に
mb_internal_encoding("UTF-8");

// クラスをインポート
use setasign\Fpdi\TcpdfFpdi;

// TCPDFの読み込み
require_once('./TCPDF/tcpdf.php');
// FPDIの読み込み
require_once('./FPDI/src/autoload.php');

/*
IPA Font (IPA Fonts 4 fonts package)   IPAfont00303.zip
|--Readme   Readme_IPAfont00303.txt
|--IPA Font License Agreement v1.0   IPA_Font_License_Agreement_v1.0.txt
|--IPAGothic   ipag.ttf
|--IPAPGothic   ipagp.ttf
|--IPAMincho   ipam.ttf
|--IPAPMincho   ipamp.ttf
*/

// フォントの読み込み
$font_path = './TCPDF/fonts/ipam.ttf';

// フォントを適用
$font = new TCPDF_FONTS();
$fontR = $font->addTTFfont($font_path);

// PDFの読み込み
$base_pdf = './PDF/sample.pdf';

// 出力するPDFの初期設定
$pdf = new TcpdfFpdi('L', 'mm', 'A4');
$pdf->setPrintHeader( false );
$pdf->setPrintFooter( false );

// テンプレートPDFの読み込み
$pdf->setSourceFile($base_pdf);

//以下PDF---------------------------------------------------------------
//1ページ目--------------------------------------------------------------
$pdf->AddPage('P', 'A4');
$pdf->useTemplate($pdf->importPage(1));

$pdf->SetFont($fontR , '', 9,'',true);
$pdf->Text( 170 , 28  ,"Test");

//2ページ目--------------------------------------------------------------
$pdf->AddPage('P', 'A4');
$pdf->useTemplate($pdf->importPage(2));


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

Google Cloud StorageとCloud SQL利用するPHPウェブサーバー

VMインストール作成

php-vm
Name: php-vm
Region: us-central1
Zone: us-central1-a
Boot disk: Debian GNU/Linux 9 (stretch)
Access scopes: Allow default access
Firewall: Allow HTTP traffic(受信HTTPトラフィックを許可します)
更に、ゾーン間のVMインスタンスを接続を参考
利用情報: VM IP addressをコピー

Cloud Storageにデータ保存

Cloud Shell開いてgsutilコマンドラインでBucket作成

mb - Make buckets

Synopsis
gsutil mb [-b <on|off>] [-c class] [-l location] [-p proj_id]
          [--retention time] url...

LOCATION変数設定するコマンド
export LOCATION=us or
export LOCATION=eu or
export LOCATION=asia
bucket作成するコマンド
gsutil mb -l $LOCATION gs://$DEVSHELL_PROJECT_ID or
gsutil mb -l us gs://bucket-name
DEVSHELL_PROJECT_ID固定変数の値はproject ID
更にgsutilのコマンドガイドを参考

cp - Copy

Synopsis
gsutil cp [OPTION]... src_url dst_url
gsutil cp [OPTION]... src_url... dst_url
gsutil cp [OPTION]... -I dst_url

[gs://cloud-training/gcpfci/]は公開するストレージデータ
:bulb:データ情報を取得するコマンド
gsutil ls gs://cloud-training/gcpfci
image.png
banner imageを作成
gsutil cp gs://cloud-training/gcpfci/my-excellent-blog.png banner-image.png
[gs://cloud-training/gcpfci/my-excellent-blog.png]:公開させる画像
[banner-image.png]:バナー画像
bucketへバナー画像をコピー
gsutil cp banner-image.png gs://$DEVSHELL_PROJECT_ID/myExcellentBlog.png
[gs://$DEVSHELL_PROJECT_ID/]:画像を保存するbucket

Gcloud SQL作成

データベース作成

Choose your database engine: MySQL
Instance ID: php-sql
Root password: @php-sql (自分が決める値)

データベース設定

  • Public IP addressをコピー
  • user accountを作成:usersタブを開いて以下の情報を設定(自分が決める値)
    • User name: phpsqluser
    • Password: phpsqluser
  • Networkを設定:Connections タブを開いて以下の情報を設定
    • name: php web
    • Password: [VM IP address]/32(32はデフォルト値)

VMインスタンスでPHPウェブサーバー構築しGCSとGSQL利用

PHPウェブサーバーインストール

php-vmのSSHを開いてPHPウェブサーバーインストール
sudo apt-get update
sudo apt-get install apache2 php php-mysql -y
sudo service apache2 restart

GSQL利用

ソースフォルダへディレクトリー
cd /var/www/html
index.phpファイルを修正する為、nanoコマンドで開く
sudo nano index.php
以下のように編集する

index.php
<html>
<head><title>Welcome to my excellent blog</title></head>
<body>
<h1>Welcome to my excellent blog</h1>
<?php
$dbserver = "CLOUDSQLIP";
$dbuser = "blogdbuser";
$dbpassword = "DBPASSWORD";
// In a production blog, we would not store the MySQL
// password in the document root. Instead, we would store it in a
// configuration file elsewhere on the web server VM instance.
$conn = new mysqli($dbserver, $dbuser, $dbpassword);
if (mysqli_connect_error()) {
        echo ("Database connection failed: " . mysqli_connect_error());
} else {
        echo ("Database connection succeeded.");
}
?>
</body></html>

「Ctrl+O → Enter → Ctrl+X」で編集が終わり

result
Database connection failed: ...

データベースへ接続できる為、index.phpファイル直し
sudo nano index.php
以下の情報を修正
dbserver = "DBのPublic IP address";
dbuser = "phpsqluser";
dbpassword = "phpsqluser";

サーバー再起動
sudo service apache2 restart

result
Database connection succeeded.

GCS利用

index.phpファイルを開いてindexページにストレージから取る画像を表示する
sudo nano index.php

index.php
<img src='myExcellentBlog.pngのLink URL'>

完成、indexページは以下のように
image.png

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

PHP+AjaxでHEAD内のOGP情報を取得する方法

[PR] e-zines(イージンズ) - 新しいスタイルでトレンドをお送りする無料のメールマガジン

いろいろな技術系のブログにOGP情報の取得方法が載ってるのだけれど
キュレーションサイトによって記事のOGP記述方法が様々なので自分で書いてみた

適当なファイル GetOGP.php とかにして ajax から呼ぶとほぼほぼ取得できます
取得できないサイト(記事)があったらコメントに書いてくれれば直しますYO!

GetOGP.php
<?php
try {
    header('content-type: application/json; charset=utf-8');
    $dat    = $_GET["url"];
    if (isset($dat) and preg_match("/^https?:/", $dat)) {
        $ctx    = stream_context_create(array(
            'http' => array(
                'method' => 'GET',
                'header' => 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36')
            )
        );
        $temp   = file_get_contents($dat, false, $ctx);
        $temp   = mb_convert_encoding($temp, "UTF-8", "auto");
        preg_match('/(<head|<HEAD)(.*)(head>|HEAD>)/s', $temp, $head);
        $html   = $head[0];

        // ogp
        $ogp    = null;
        preg_match_all("<meta\s*property=[\"']og:([^\"']+)[\"']\s*content=[\"']([^\"']+)[\"']\s*>", $html, $ogp);
        for( $i = 0; $i < count($ogp[1]); $i++ ) {
            $result[$ogp[1][$i]] = $ogp[2][$i];
        }

        $ogp    = null;
        preg_match_all("<meta\s*content=[\"']([^\"']+)[\"']\s*property=[\"']og:([^\"']+)[\"']\s*>", $html, $ogp);
        for( $i = 0; $i < count($ogp[1]); $i++ ) {
            $result[$ogp[2][$i]] = $ogp[1][$i];
        }

        $ogp    = null;
        preg_match_all("<meta\s*name=[\"']og:([^\"']+)[\"']\s*content=[\"']([^\"']+)[\"']\s*>", $html, $ogp);
        for( $i = 0; $i < count($ogp[1]); $i++ ) {
            $result[$ogp[1][$i]] = $ogp[2][$i];
        }

        // twitter
        $twitter    = null;
        preg_match_all("<meta\s*name=[\"']twitter:([^\"']+)[\"']\s*content=[\"']([^\"']+)[\"']\s*>", $html, $twitter);
        for($i = 0; $i < count($twitter[1]); $i++) {
            $result2[$twitter[1][$i]] = $twitter[2][$i];
        }

        if (count($twitter[1]) == 0) {
            preg_match_all("<meta\s*content=[\"']([^\"']+)[\"']\s*name=[\"']twitter:([^\"']+)[\"']\s*>", $html, $twitter);
            for($i = 0; $i < count($twitter[1]); $i++) {
                $result2[$twitter[2][$i]] = $twitter[1][$i];
            }
        }

        if (!isset($result['title'])) {
            $result['title']    = $result2['title'];
        }
        if (!isset($result['url'])) {
            if (isset($result2['url'])) {
                $result['url']  = $result2['url'];
            } else {
                $result['url']  = $url;
            }
        }
        if (!isset($result['image'])) {
            if (isset($result2['image:src'])) {
                $result['image']    = $result2['image:src'];
            } else {
                $result['image']    = $result2['image'];
            }
        }
        if (!isset($result['description'])) {
            $result['description']  = $result2['description'];
        }
        if (!isset($result['site_name'])) {
            $result['site_name']    = $result2['site'];
        }

        $res    = [
            'title'         => str_replace('"', '”', $result['title']),
            'url'           => str_replace('http://', 'https://', $result['url']),
            'image'         => str_replace('http://', 'https://', $result['image']),
            'description'   => str_replace('"', '”', $result['description']),
            'site_name'     => str_replace('"', '”', $result['site_name']),
        ];

        // success
        echo json_encode($res);
    } else {
        // fail
        echo json_encode(['status' => 'error']);
    }
} catch (Exception $e) {
    // fail
    echo json_encode([
            'status' => 'fatal error:'.$e->getMessage(),
    ]);
}

では、また

[PR] e-zines(イージンズ) - 新しいスタイルでトレンドをお送りする無料のメールマガジン

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

ラズパイでPHPを導入してみた

はじめに

まずは恒例の

sudo apt-get update

をしてからPHPのインストール

sudo apt-get install php php-xml

インストールが完了したら、バージョンを確認してみる

php -v

Screenshot from 2020-02-18 09-03-25.png
こんな感じにバージョンが表示されたらインストール出来てる

とりあえずなにか表示

phpinfo();を表示してみる
HTMLのときと同じようにファイルを作って中身を入力していく。ファイル名は「test」にする。

pi@raspberrypi:~ $ sudo vim /var/www/html/test.php
<?php
    phpinfo();
?>

で、HTMLと同じ時の要領で
http://192.168.*.*/test.php
で表示してみる。

・・
・・・
なぜか表示できない...
どういう訳かそのまま
<?php
phpinfo();
?>

が表示されちゃう...

解決策

色々調べて試してみたが、どうやってもうまく行かないので最後の手段として一回全部削除してみる…

 sudo apt-get --purge remove php*

そして最初から同じ手順を試してみる

Screenshot from 2020-02-18 09-52-58.png
やっと表示できた。

原因はよくわからないけど、詰んだらとりあえず真っさらな状態にして一度試すしかない(正しいかわからないけど)

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

iksm_sessionを取得するための認証リンク

ほしかったもの

Swiftでニンテンドーアカウントにアクセスしてiksm_sessionを取得したかった。

splatnet2statinkでは実装されているのだが、そのコードをPHP向けに書き直した感じです。

ただ、これだけでは足りなくてNexus氏のflapg APIとfrozenpandaman氏のs2s APIが別途必要になります。

実装

PHPでイカのコードを書くだけ。

<?php
function urlsafe_b64encode($val) {
  $val = base64_encode($val);
  return str_replace(["/","+","="], ["_","-",""], $val);
}
$auth_state = urlsafe_b64encode(random_bytes(36));
$auth_code_verifier = urlsafe_b64encode(random_bytes(32));
$auth_cv_hash = hash("sha256", $auth_code_verifier);
$auth_code_challenge = urlsafe_b64encode(hex2bin($auth_cv_hash));

$base_url = "https://accounts/nintendo.com/connect/1.0.0/authorize?";
$base_url = "https://accounts.nintendo.com/connect/1.0.0/authorize?";

$param = array( 
    "state" => $auth_state,
    "redirect_uri" => "npf71b963c1b7b6d119://auth",
    "client_id" => "71b963c1b7b6d119",
    "scope" => "openid user user.birthday user.mii user.screenName",
    "response_type" => "session_token_code",
    "session_token_code_challenge" => $auth_code_challenge,
    "session_token_code_challenge_method" => "S256",
    "theme" => "login_form"
);
$auth_url = $base_url . http_build_query($param);
$response = array(
    "auth_code_verifier" => $auth_code_verifier,
    "auth_url" => $auth_url);

header('content-type: application/json; charset=utf-8');
echo(json_encode($response, JSON_UNESCAPED_SLASHES));
?>

リダイレクトURIがURLエンコードされてしまうので微妙にダサい感じになるがこれで動きます。

完成したもの

自分のリリースしたアプリでどうしてもそういう機能が欲しかったのでこういうのを完成させました。

AWS上のサーバなのでご利用は計画的に。

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

PHPerからGo使いへ

Goとは?

・Goは2009年にGoogleが開発したオープンソースプロジェクトのプログラミング言語です。

特徴

  • いわゆる静的言語 ただし型推論は強め
  • シンプルな記述
    • ループ処理はfor文だけ
    • 他の言語で用意されている継承の概念がない
  • 並行処理の記述が簡単
  • 実行速度やコンパイル速度が早い

触ってみた所感

  • Javaなどの他の静的言語に比べて記述が簡単!
  • PHPと比べると実行速度早いのは良いです。
  • よくも悪くもコンパイルが必要な言語なので、実行前にポカミスが洗い出されるのは良いです。
  • シンプルで出来ることが他の言語に比べ限られているので、記述ややり方について悩むことは少ないのでは、と想像。

サンプルコード

fizzbuzz.go
package main

//ライブラリの読み込み。これをPHPで言う組み込み関数的な。
import (
    "fmt"
    "strconv"
    )

//main関数から実行されます。
//Goにはクラスの概念はありません。またファイル実行のためには必ずfunc main()が必要です。
func main(){

//厳密な変数宣言。
var num int
num = 10000

//main関数から別の関数を呼び出し。
message := fizzbuzz(num)

    fmt.Println(message)
}

//引き数に型の指定はマスト。
//返り値がある場合には返り値の型も書かないといけません。
func fizzbuzz(num int) string{

var message string   
//このように省略して変数宣言もできます。型の指定も省略できます。
for i := 1; i <= num; i++{
    if i % 3 == 0 && i % 5 == 0 {
        //文字列の結合は+を使います。
        message = message + "FizzBuzz\n"
    } else if i % 3 == 0 {
        message = message + "Fizz\n"
    } else if i % 5 == 0 {
        message = message + "Buzz\n"
    } else {
        //整数と浮動小数点数のキャストは簡単なのですが、
        //整数と文字列のキャストはこのように厳密にやらなくてはいけません。
        var s string
        s = strconv.Itoa(i)
        message = message + s + "\n"
    }
} 
return message
}

ちょっとした考察

  • WEBアプリなどで言語の処理速度がボトルネックになることはまずない(DBがボトルネックになりやすい)ので、速度を目的にしてGoをチョイスするのは微妙かなと思いました。(DBとかが介在しない自動売買のBOTとかには良いです。)
  • PHPにあってGoにないものとしてフレームワークの存在があります。 言語として記述をシンプルにしているのでフレームワークは(現状)不要、と言う考え方もできますが、 PHPなどの他の言語ではフレームワークを使用することによって、記述のシンプルさや統一性を担保しているとも言えるので、この点でもGoの優位性は微妙かなと思いました。(個人的にはGoそもそものシンプルさは好きです。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【docker】ERROR:OCI runtime create failedが出る。

状況

Dockerfile
FROM php:7.3-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
  && apt-get install -y zlib1g-dev libzip-dev mariadb-client \
  && docker-php-ext-install zip pdo_mysql

#Composer install
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin

RUN composer global require "laravel/installer"

php:7.3-fpmのDockerfile下のように書き、docker-compose up -d --buildすると、次のようなエラーが出る。

ERROR: for php-fpm  Cannot start service php-fpm: OCI runtime create failed: container_linux.go:344: starting container process caused "chdir to cwd (\"/var/www/html\") set in config.json failed: permission denied": unknown
ERROR: Encountered errors while bringing up the project.

chdir to cwd (\"/var/www/html\")などを見るとphp-fpmの作業ディレクトリの問題だろうと思いDockerfileを修正する。

Dockerfile
WORKDIR /var/www 

今回はアプリを/var/wwwに置いているので、そこを作業ディレクトリに指定してみる。
再びdocker-compose up -dしてみると無事コンテナが起動する。

原因

単純にDockerfileに作業ディレクトリの指定がなく、/var/www/htmlとなっていたこと。
なぜ/var/www/htmlとなっていたかはわからない。デフォルトではそうなるのだろうか。

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

非階層クラスター分析のk-means法をPHPで作ってみた

非階層クラスター分析で代表的な手法のk-means法は、pythonのライブラリに存在したり、統計ソフトに標準機能として搭載されていることが多く、なじみの多い手法のようですが、実際のアルゴリズムを確認するために、(慣れている)PHPで作ってみました。

k-means法のアルゴリズムの理解は、こちらがわかりやすいです。

ソースは以下の通りです。

点を2次元平面に点在させて、それらのクラスターに分けます。
完全にランダムに配置させると、クラスター化の初期値依存が高くなるため、敢えていくつかの場所に集中して配置させるようにしました。

<?php

$max = 500; //領域の大きさ
$margin = 50; //ランダムの配置するときの中心からの範囲
$dot_num_per_center = 30; //1中心点に配置するときの点の数
$center_num = 4; //中心点の数
$action_num = 5; //k-means実行回数

//点の配置
print "①この下の表が配置した点です。";
$dots = array();
print "<table>";
for ($i = 1; $i <= $center_num; $i++) {
    //中心点の位置をランダムに決める
    $center_x = rand($margin, $max - $margin);
    $center_y = rand($margin, $max - $margin);
    for ($j = 1; $j <= $dot_num_per_center; $j++) {
        //中心点を中心にmarginの範囲で点を決める
        $x = rand($center_x - $margin, $center_x + $margin);
        $y = rand($center_y - $margin, $center_y + $margin);
        $dots[] = ["x" => $x, "y" => $y];
        print "<tr><td>{$x}</td><td>{$y}</td></tr>";
    }
}
print "</table>";

//初期の重心を適当に決める(適当すぎて重心候補から外れてエラーになることがある)
$gravity = [];
for ($i = 1; $i <= $center_num; $i++) {
    $gravity[$i]["x"] = rand($margin, $max - $margin);
    $gravity[$i]["y"] = rand($margin, $max - $margin);
}

//k-means実行
for($num = 1; $num <= $action_num; $num++)
{
    //各点の一番近い重心を決める
    foreach ($dots as $dot_key => $dot) {
        $min_length = 0;
        for ($i = 1; $i <= $center_num; $i++) {
            $length = calc_length($gravity[$i]["x"], $gravity[$i]["y"], $dot["x"], $dot["y"]);
            if($min_length === 0 || $min_length > $length) {
                $min_length = $length;
                $dots[$dot_key]["gravity_number"] = (int)$i;
            }
        }
    }

    //各重心の点の重心を再計算する
    $gravity = [];
    $count = [];
    foreach ($dots as $dot_key => $dot) {
        if(!isset($gravity[$dot["gravity_number"]]["x"])) $gravity[$dot["gravity_number"]]["x"] = 0;
        $gravity[$dot["gravity_number"]]["x"] += $dot["x"];
        if(!isset($gravity[$dot["gravity_number"]]["y"])) $gravity[$dot["gravity_number"]]["y"] = 0;
        $gravity[$dot["gravity_number"]]["y"] += $dot["y"];

        //核にある点の数
        if(!isset($count[$dot["gravity_number"]]))$count[$dot["gravity_number"]] = 0;
        $count[$dot["gravity_number"]]++;
    }

    for ($i = 1; $i <= $center_num; $i++) {
        if(isset($gravity[$i]["x"]))$gravity[$i]["x"] /= $count[$i];
        if(isset($gravity[$i]["y"]))$gravity[$i]["y"] /= $count[$i];
    }
}

print "<br /><br /><br />②以下がクラスターに分けたデータです";

//それぞれの重心の点を表示する
print "<table>";
foreach ($dots as $dot_key => $dot) {
    print"<tr><td>{$dot["x"]}".(str_repeat("</td><td>", (int)$dot["gravity_number"])).$dot["y"]."</td></tr>";
}
print "</table>";

//長さを求める
function calc_length($x1, $y1, $x2, $y2)
{
    return sqrt(pow($x1 - $x2, 2) + pow($y1 - $y2, 2));
}

データの見方

上記PHPを実行すると、上下に2つの表が出力します。(①②と表示しています)

データの点在確認

①はデータの点在を確認します。エクセルに貼り付けて、「散布図」にて確認出来ます。
名称未設定-1.png

クラスターの確認

②はクラスター分けされた状態を確認できます。同じくエクセルに貼り付けて、「散布図」にて確認出来ます。
名称未設定-2.png

課題

仮に決めた重心が完全ランダムですので、データの点在から離れすぎてエラーとなることがあります。。。
その時は、再度実行して下さい。
実際のk-means法では、仮決めの重心はデータの点在状況に応じて、ある程度領域を限定するものだとわかりました。

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