tohokuaikiのチラシの裏

技術的ネタとか。

PHPの関数内で自己再帰呼び出し

できるかな?と思ってテスト…

<?php
function r($count)
{
    echo $count."\n";
    if ($count < 10) {
        r(++$count);
    }
}
r(1);

1
2
3
4
5
6
7
8
9
10

でも、関数名を変えると再帰呼び出ししてるところも変えないといけない。

__FUNCTION__を使う

関数内では、__FUNCTION__が関数名を示すので

<?php
function r($count)
{
    echo $count."\n";
    if ($count < 10) {
        __FUNCTION__(++$count);
    }
}

ってやった…Parse Error…ですよねー。

一旦変数にする。

<?php
function r($count)
{
    $f_name = __FUNCTION__;
    echo $count."\n";
    if ($count < 10) {
        $f_name(++$count);
    }
}

これはうまくいった。だけど…だけどですよねー。JavaScriptならarguments.callee使えるのになー。

(function(c){ 
    console.log(c++);
     if (c<10) {
        arguments.callee(c);
    }
})(1)

PHPのClosureは変数に入れてるしなー。ダメっぽいな。

MovableTypeの$__value__に気をつける。

特殊変数のvalue

ループで使えるんだけど、

<mt:SetHashVar name="month">
<mt:SetVar name="Jan" value="January">
<mt:SetVar name="Feb" value="February">
</mt:SetHashVar>

<mt:Loop name="month" sort_by="value">
  <li><mt:Var name="__key__">: <mt:Var name="__value__"></li>
</mt:Loop>

https://www.movabletype.jp/documentation/appendices/tags/loop.html

なんと、MTContentFieldでも使えるのであった…

<mt:Contents content_type="イベント・セミナー" limit="1">
    <mt:ContentField content_field="開催日時"><mt:ContentFieldValue></mt:ContentField>
</mt:Contents>

という感じだけど、

<mt:Contents content_type="イベント・セミナー" limit="1">
    <mt:ContentField content_field="開催日時"><Var name="__value__"></mt:ContentField>
</mt:Contents>

でもいけてしまうのであった…。 https://www.movabletype.jp/documentation/appendices/tags/contentfield.html

Loopの中で_value_使ってた自分、_value_がMTContentFieldで上書きされていることに気づかず無為な時間を過ごしてしまった…

そして、普通に_value_と書くと、はてブではvalueになってしまうのか…_\_value_\_と書かねばならないのだな。

Let's Encryptがまたまたこけてた。

ブログの記事にするのは3回目。

2020-01-14 certbotがまたこけてた - tohokuaikiのチラシの裏
2019-12-09 Debian10でCertbotがこける - tohokuaikiのチラシの裏

なんか、こういうのがあってあんまりクリティカルかつあまりメンテナンスをしないサーバーではLet's Encryptを入れてないんだけど。

エラー内容

こんな感じ

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/xxxx.org.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for xxxxx.org
Cleaning up challenges
Attempting to renew cert (xxxx.org) from /etc/letsencrypt/renewal/xxxx.org.conf produced an unexpected error: Missing command line flag or config entry for this setting:
Input the webroot for xxxx.org:. Skipping.

なんか、webrootが無いって言われる。なんで今更…

ということで、対応

/etc/letsencrypt/renewal/xxxx.org.conf に

[[webroot_map]]
xxxx.org = /var/www/xxxx.org/htdocs

というのを入れてDocumentRootを指定すると上手くいった。

Google AnalyticsのデータをAPI経由で取り出すとか

Google AnalyticsのデータをAPI経由で取り出すとかできるらしい。しかも随分と昔から…Version4ってどんだけ昔からだったんだという…

これに従ってやっていきます。

Google Cloud Platformでプロジェクトを作る

最近のGoogleはみんなGoogle Cloud Platformですわー。

f:id:tohokuaiki:20200114181217p:plain

アカウントの追加

でこのプロジェクトを操作できるアカウントを追加する。

f:id:tohokuaiki:20200114181604p:plain

ときて

f:id:tohokuaiki:20200114181703p:plain

サービス アカウント名とサービス アカウントの説明を入力して作成。

f:id:tohokuaiki:20200114181855p:plain

で、このアカウントにロールを付与。とりあえずオーナー入れておけばいいんじゃないのっていう。

f:id:tohokuaiki:20200114181943p:plain

で、キーの作成→JSONでキーファイルがダウンロードされる。

f:id:tohokuaiki:20200114182035p:plain

これでアカウント追加完了。

Google Analyticsの管理画面で先ほど作成したアカウントに権限を与える

GoogleAnalyticsの管理画面に行き、ビューを選び「管理」の所から「ユーザー管理を表示」を選択。

f:id:tohokuaiki:20200114182451p:plain

右上の「+」から先ほど作ったアカウントのメールアドレスを入れる。権限は、「表示と分析」だけでいいでしょう。

f:id:tohokuaiki:20200114182634p:plain

PHPのライブラリをインストールして、サンプルを動かす

ファイルの設置

PHPが使えるサーバーに入って、コマンドラインからcomposerを使ってGoogleが提供してるPHPライブラリを

composer require google/apiclient:^2.0

とインストールする。

既にGoogleがサンプルを用意してくれてるので、ここから取得。HelloAnalytics.phpというファイルが得られるのでサーバーに送る。

アカウントのセットアップ

先ほどの「キーを作成」で取得したjsonファイルを service-account-credentials.json としてHelloAnalytics.phpと同じところにアップする。

HelloAnalytics.phpの44行目当たりにある、

<?php
function getReport($analytics) {

  // Replace with your view ID, for example XXXX.
  $VIEW_ID = "<REPLACE_WITH_VIEW_ID>";

の<REPLACE_WITH_VIEW_ID>を取得する。https://ga-dev-tools.appspot.com/account-explorer/?hl=ja に移動してIDを取得する。

この作業をしたら、HelloAnalytics.phpを動かす。

$ php HelloAnalytics.php
PHP Fatal error:  Uncaught Google_Service_Exception: {
  "error": {
    "code": 403,
    "message": "Analytics Reporting API has not been used in project 32xxxxxxx8237 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/analyticsreporting.googleapis.com/overview?project=329609518237 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "errors": [
      {
        "message": "Analytics Reporting API has not been used in project 32xxxxxxx8237 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/analyticsreporting.googleapis.com/overview?project=329609518237 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
        "domain": "usageLimits",
        "reason": "accessNotConfigured",
        "extendedHelp": "https://console.developers.google.com"
      }
    ],
    "status": "PERMISSION_DENIED"
  }
}

…と、エラーが出る。

URLが表示されるので、アクセスすると

f:id:tohokuaiki:20200114184136p:plain

とでるので、「有効にする」を押す。

…と、ここまでで動くようになったけど、なんかデータ取れなかった。

なんか、サンプルのHelloAnalytics.phpがおかしい

このgetReportが

<?php
function getReport($analytics) {

  // Replace with your view ID, for example XXXX.
  $VIEW_ID = "12345678";

  // Create the DateRange object.
  $dateRange = new Google_Service_AnalyticsReporting_DateRange();
  $dateRange->setStartDate("7daysAgo");
  $dateRange->setEndDate("today");

  // Create the Metrics object.
  $sessions = new Google_Service_AnalyticsReporting_Metric();
  $sessions->setExpression("ga:sessions");
  $sessions->setAlias("sessions");

  // Create the ReportRequest object.
  $request = new Google_Service_AnalyticsReporting_ReportRequest();
  $request->setViewId($VIEW_ID);
  $request->setDateRanges($dateRange);
  $request->setMetrics(array($sessions));

  $body = new Google_Service_AnalyticsReporting_GetReportsRequest();
  $body->setReportRequests( array( $request) );
  return $analytics->reports->batchGet( $body );
}

ってなってるけど、この記事を見ると $dimentionと$orderbyを入れているし…

<?php
function getReport($analytics) {

  // Replace with your view ID, for example XXXX.
  $VIEW_ID = "88097852";
//  $VIEW_ID = "52492899";

  // Create the DateRange object.
  $dateRange = new Google_Service_AnalyticsReporting_DateRange();
  $dateRange->setStartDate("7daysAgo");
  $dateRange->setEndDate("today");

  // Create the Metrics object.
  $sessions = new Google_Service_AnalyticsReporting_Metric();
  $sessions->setExpression("ga:sessions");
  $sessions->setAlias("sessions");

  $dimention = new Google_Service_AnalyticsReporting_Dimension();
  $dimention->setName( 'ga:landingPagePath' );

  $orderby = new Google_Service_AnalyticsReporting_OrderBy();
  $orderby->setFieldName( "ga:sessions" );
  $orderby->setOrderType( "VALUE" );
  $orderby->setSortOrder( "DESCENDING" );

  // Create the ReportRequest object.
  $request = new Google_Service_AnalyticsReporting_ReportRequest();
  $request->setViewId($VIEW_ID);
  $request->setDateRanges($dateRange);
  $request->setMetrics(array($sessions));

  $request->setDimensions( array( $dimention ) );
  $request->setOrderBys( $orderby );    

  $body = new Google_Service_AnalyticsReporting_GetReportsRequest();
  $body->setReportRequests( array( $request) );
  return $analytics->reports->batchGet( $body );
}

これでPHP実行するとパスごとのセッション数が取得できた。

certbotがまたこけてた

certbot renewすると

The requested apache plugin does not appear to be installed. Skipping.

とエラー出して更新してくれない。

ログ /var/log/letsencrypt/letsencrypt.log を見ると

020-01-12 09:32:53,247:WARNING:certbot.renewal:Attempting to renew cert (www.example.jp) from /etc/letsencrypt/renewal/www.example.jp.conf produced an unexpected error: The requested apache plugin does not appear to be installed. Skipping.
2020-01-12 09:32:53,286:DEBUG:certbot.renewal:Traceback was:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 452, in handle_renewal_request
    main.renew_cert(lineage_config, plugins, renewal_candidate)
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1187, in renew_cert
    installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly")
  File "/usr/lib/python3/dist-packages/certbot/plugins/selection.py", line 237, in choose_configurator_plugins
    diagnose_configurator_problem("authenticator", req_auth, plugins)
  File "/usr/lib/python3/dist-packages/certbot/plugins/selection.py", line 341, in diagnose_configurator_problem
    raise errors.PluginSelectionError(msg)
certbot.errors.PluginSelectionError: The requested apache plugin does not appear to be installed

2020-01-12 09:32:53,286:ERROR:certbot.renewal:All renewal attempts failed. The following certs could not be renewed:
2020-01-12 09:32:53,287:ERROR:certbot.renewal:  /etc/letsencrypt/live/www.example.jp/fullchain.pem (failure)
2020-01-12 09:32:53,287:DEBUG:certbot.log:Exiting abnormally:
Traceback (most recent call last):
  File "/usr/bin/certbot", line 11, in <module>
    load_entry_point('certbot==0.31.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1365, in main
    return config.func(config, plugins)
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1272, in renew
    renewal.handle_renewal_request(config)
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 477, in handle_renewal_request
    len(renew_failures), len(parse_failures)))
certbot.errors.Error: 1 renew failure(s), 0 parse failure(s)

ってなってる。

Apacheのモジュールが必要って、何やねん…って思ったけど、 にあった通り、

apt install python3-certbot-apache

したら直った。