1. 記事一覧 >
  2. ブログ記事
category logo

php→pythonのトランスパイル 人力読み替え編

(更新) (公開)

はじめに

PHPのプログラム(wiringpi-php-bme280(GitHub))を同一の機能のままPythonに変換し、wiringpi-python-bme280(GitHub)に公開しました。
別記事の「php→pythonのトランスパイル」のプログラムを使って文法をある程度変換→人力で変換と作業しましたが、結局、ほとんどが人力作業だったような気がします。今回、備忘録的にphp→pythonの変換例を列挙していきます。

細かい説明は省略し、変換前、変換後の列挙に留めます。順番は適当です。場合によっては等価ではないこともありますので、PHP、Python双方を理解して参考程度としてください。また、この記事中にあってもwiringpi-python-bme280では、他の方法で書き換えたものもあります。

検証環境は、

Raspbian GNU/Linux 10 (buster)

Python 3.7.3

PHP 7.3.29-1

になります。


【目次】
definerequire&&argc!strcasecmpintvalfile_get_contentsposix_killis_filepcntl_forkdie$_SERVERemptymkdir$_REQUESTopendir readdiris_filepreg_match[] 配列追加rsortsubstrissetfilearray_shiftexplodefloatval連想配列のキーと値追加trimjson_encodecurl_initcurl_setopt$_POSTis_arrayforeach引数のデフォルト値array()apache_request_headersstrlenstatic$GLOBALSprintfflushstrposheadercurl_execcurl_closeusleeptimestrftimesprintffile_put_contentsfseekftellpcntl_signal無名関数__constructprotectedfor>>++文字コード指定ord三項演算子


define

define( "LOG_DIR",	"/var/log/bme280log/" );

LOG_DIR = "/var/log/bme280log/"

require

require( "bme280.inc" );

import bme280_inc

※bme280_inc.py読み込み


&&

if ( $argc > 1 && !strcasecmp( $argv[1], "stop" ) ) {

if len(sys.argv) > 1 and sys.argv[1].lower() != "stop":

argc

if ( $argc > 1 && !strcasecmp( $argv[1], "stop" ) ) {

if len(sys.argv) > 1 and sys.argv[1].lower() != "stop":

!

if文のエクスクラメーション/ビックリマーク

if ( $argc > 1 && !strcasecmp( $argv[1], "stop" ) ) {

if len(sys.argv) > 1 and sys.argv[1].lower() != "stop":
if( !is_file( "$dir/$file" ) ) continue;

if not os.path.isfile(os.path.join(dir, file)):
    continue

strcasecmp

if ( !strcasecmp( $_SERVER["REQUEST_METHOD"], "POST" ) ) {

if request.environ["REQUEST_METHOD"].upper() == "POST":

※lowerまたは、upper


intval

$pid = intval( file_get_contents( PID_FILE ) );

pid = int(file_get_contents(PID_FILE))

file_get_contents

$pid = intval( file_get_contents( PID_FILE ) );

def file_get_contents(filename):
    with open(filename) as f:
        return f.read()

pid = int(file_get_contents(PID_FILE))

posix_kill

$pid = intval( file_get_contents( PID_FILE ) );
posix_kill( $pid );

import os
import signal
os.kill(pid, signal.SIGTERM)

is_file

if is_file(PID_FILE):

if os.path.isfile(PID_FILE):

pcntl_fork

pid = pcntl_fork()
if pid==-1:
  die("fork できません")

try:
  pid = os.fork()
except OSError:
  print("fork できません")
  sys.exit()

die

die("fork できません")

print("fork できません")
sys.exit()

$_SERVER

if ( empty( $_SERVER["REQUEST_METHOD"] ) || $_SERVER["REQUEST_METHOD"] != "POST" ) {

from flask import request
if (
    request.environ["REQUEST_METHOD"] is None
    or request.environ["REQUEST_METHOD"] != "POST"
):

empty

if ( empty( $_SERVER["REQUEST_METHOD"] ) || $_SERVER["REQUEST_METHOD"] != "POST" ) {

if (
    request.environ["REQUEST_METHOD"] is None
    or request.environ["REQUEST_METHOD"] != "POST"
):
if ( !empty( $GLOBALS["CHUNKED"] ) ) {

if hasattr(builtins, "CHUNKED") and builtins.CHUNKED:

mkdir

mkdir( $dir, 0755 );

os.mkdir(dir)
os.chmod(dir, 0o755)

$_REQUEST

if ( $_REQUEST["TYPE"] == "FILELIST" ) {

from flask import request
if request.form.get("TYPE") == "FILELIST":

opendir readdir

$dp = opendir( $dir );
if ( $dp !== false ) {
	while( ( $file = readdir( $dp ) ) !== false ) {

for f in os.listdir(dir):

is_file

if( !is_file( "$dir/$file" ) ) continue;

if not os.path.isfile(os.path.join(dir, f)):

preg_match

if( preg_match( "/\.log$/", $file ) ) {

import re
matches = re.search('\.log$', file)

※大文字小文字無視フラグ有りの場合

if ( preg_match( "/^Transfer\-Encoding\:\s+chunked/i", $head ) ) {

if re.search(
    r"^Transfer\-Encoding\:\s+chunked", head, re.IGNORECASE
):

[] 配列追加

$files[] = $file;

files.append(file)

rsort

rsort( $files );

files.sort(reverse=True)

substr

$date = substr( $file, 0, 4 ) . "/" .

date = file[0:4] + "/" 

※file[0:4]は、file[:4]でも良い。


isset

if( isset( $_REQUEST["DATE"] ) ) {

if request.form.get("DATE") is not None:

file

$logdata = file( $file, FILE_IGNORE_NEW_LINES );
//FILE_IGNORE_NEW_LINES : 配列の各要素の最後に改行文字が含まれない

try:
    with open(file,"r") as f:
        logdata = f.read().splitlines()
except:
    pass

array_shift

array_shift( $logdata );

logdata.pop(0)

explode

$val = explode( "\t", $data );

val = data.split("\t")

floatval

$result[$val[1]] = array(
  floatval( $val[2] ), floatval( $val[3] ), floatval( $val[4] )
);

result[val[1]] = [float(val[2]), float(val[3]), float(val[4])]

連想配列のキーと値追加

$result[$val[1]] = array(
  floatval( $val[2] ), floatval( $val[3] ), floatval( $val[4] )
);

result[val[1]] = [float(val[2]), float(val[3]), float(val[4])]

trim

$data = trim( file_get_contents( $file ) );

data = file_get_contents(file).strip()

json_encode

$text = json_encode( $result, JSON_UNESCAPED_UNICODE );
//JSON_UNESCAPED_UNICODE:マルチバイト Unicode 文字をそのままの形式で扱います (デフォルトでは \uXXXX にエスケープします)。

import json
text = json.dumps(result, ensure_ascii=False)

from flask import Flask
app = Flask(__name__)
app.config["JSON_AS_ASCII"] = False
return jsonify(result)

curl_init

$ch = curl_init();

import pycurl
ch = pycurl.Curl()

curl_setopt

curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );

ch.setopt(pycurl.SSL_VERIFYPEER, False)

$_POST

$param = array_to_str( $_POST );

from flask import request
param = array_to_str(request.form)

is_array

if ( !is_array( $value ) ) {

if not isinstance(value, (list, tuple, set, dict)):

foreach

foreach( $value as $k => $v ) {

for k, v in value:

引数のデフォルト値

function array_to_str( $value, $key = "" ) {

def array_to_str(value, key=""):

array()

$headers = array();

headers = []

apache_request_headers

foreach ( apache_request_headers() as $name => $value ) {

import os
for name, value in os.environ.iteritems():
    if name.startswith("HTTP_")

from flask import request
for name, value in request.headers:

strlen

$ln = strlen( $buffer );

ln = len(buffer)

static

function call_back( $ch, $buffer ) {
    $ln = strlen( $buffer );
    static $start_head = false;
    static $start_body = false;
    static $save = "";

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

@static_vars(start_head=False,start_body=False,save="")
def call_back(ch, buffer):
    ln = len(buffer)

$GLOBALS

スーパーグローバル(super global)

if ( !empty( $GLOBALS["CHUNKED"] ) ) {

import builtins
if hasattr(builtins, "CHUNKED") and builtins.CHUNKED:


メモリに留まり続けるため、globalステートメント使用

global chunked_flag
chunked_flag = False
def call_back(buffer):
    ln = len(buffer)
    global chunked_flag
    if call_back.start_body:
        if chunked_flag:

printf

printf( "%x\r\n", strlen( $buffer ) );

print("%x\r\n" % (len(buffer)), end='')

flush

flush();

import sys
sys.stdout.flush()

from flask import flash
flash('xxx')

※未検証


strpos

if ( ( $pos = strpos( $buffer, "\r\n" ) ) === false ) {

pos = buffer.find("\r\n")
if pos > 0:

header

header(head)

print(head)
print()
header('HTTP/1.1 400 Bad Request');
echo "Bad Request";
exit;

from flask import abort
abort(400, 'Bad Request')
header( 'HTTP', true, 403 );
echo "Could not connect to server\n'{$_REQUEST["REQUEST_URL"]}'\r\n";

return "Could not connect to server\n'" + request.form.get("REQUEST_URL") + "'\r\n",403

Flaskでjsonを返す場合は、以下。

$text = json_encode( $result, JSON_UNESCAPED_UNICODE );
header( "Content-Type: application/json; charset=utf-8" );
header( "Content-Length: " . strlen( $text ) );
echo $text;

from flask import jsonify

app.config["JSON_AS_ASCII"] = False
@app.route("/getlogdata")
def getlogdata():
    return jsonify(result)

curl_exec

if ( curl_exec( $ch ) !== true ) {
    # エラー処理

try:
    ch.perform()
except Exception:
    # エラー処理

curl_close

curl_close( $ch );

ch.close()

usleep

usleep( 100000 );

import time
def usleep(x):
    time.sleep(x/1000000.0)
usleep(100000)

import time
time.sleep(100000 / 1000000.0)

time

$tm = time();

import time
tm = int(time.time())

strftime

$text .= strftime( "%Y/%m/%d\t", $tm );

import datetime
dt_now = datetime.datetime.now()
text += dt_now.strftime( "%Y/%m/%d\t")

sprintf

$text .= sprintf( "%.2f\t", $bme280->temperature + 0.005 );

text += "{0:.2f}\t".format(bme280.temperature + 0.005)

file_put_contents

LOCK_EX(排他ロック)有り

file_put_contents( $log_file, $text, LOCK_EX  );

import fcntl
def file_put_contents(filename, data):
    with open(filename, "w") as f:
        f.write(data)
def touch(filename):
    if os.path.exists(filename):
        # os.utime(fname, None)
        pass
    else:
        open(filename, "a").close()
def file_put_contents_ex(filename, data):
    touch(filename)
    with open(filename) as lockfile:
        fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX)
        try:
            file_put_contents(filename, data)
        finally:
            fcntl.flock(lockfile.fileno(), fcntl.LOCK_UN)

fseek

$fp = fopen( $log_file, "a" );
if ( $fp !== false ) {
	fseek( $fp, 0, SEEK_END );

with open(log_file, "a") as f:
    pos = f.tell()

※"a"フラグのopenで自動的にSEEK_ENDになる。


ftell

$pos = ftell( $fp );

pos = f.tell()

pcntl_signal

pcntl_signal( SIGTERM, function( $signo, $siginfo ) {
	$GLOBALS["lcd"]->lcdClear();
	$GLOBALS["lcd"]->lcdDisplay( false );
	unlink( PID_FILE );
	exit;
} );

signal.signal(
    signal.SIGTERM,
    lambda _signo, _stack_frame: [lcd.lcdClear(), lcd.lcdDisplay(False), os.unlink(PID_FILE), sys.exit()],
)

※signal.signalの2つ目の引数のcallback関数は、引数を2つ取る。


無名関数

pcntl_signal( SIGTERM, function( $signo, $siginfo ) {
	$GLOBALS["lcd"]->lcdClear();
	$GLOBALS["lcd"]->lcdDisplay( false );
	unlink( PID_FILE );
	exit;
} );

signal.signal(
    signal.SIGTERM,
    lambda _signo, _stack_frame: [lcd.lcdClear(), lcd.lcdDisplay(False), os.unlink(PID_FILE), sys.exit()],
)

__construct

function __construct( $addr, $rows = 2, $cols  =16, $bits = 4 ) {

def __init__(self, addr, rows=2, cols=16, bits=4):

protected

protected $rows;

_rows = 2

for

for ( $i = 0; $i < 8; $i ++ ) {

for i in range(8):

>>

ビット演算子右シフト

$this->put4Command( $func >> 4);

self.put4Command(func >> 4)

++

インクリメンタル演算子

if ( ++ $this->cx >= $this->cols ) {

self._cx += 1
if self._cx >= self._cols: 

文字コード指定

\xdf sjis文字コード指定

$lcd->lcdPuts( sprintf( "%5.2f\xdfC   %5.2f%%", $bme280->temperature + 0.005, $bme280->humidity + 0.005 ) );

lcd.lcdPuts(
    "{0:5.2f}{1}C   {2:5.2f}%".format(
        bme280.temperature + 0.005, b"\xdf".decode('sjis'), bme280.humidity + 0.005
    )
)

※ \xdf は、半角の丸です。 1602A LCDは、ANKコードを使用するため、℃の表示にsjisの半角丸を使っています。

【 ANKコード 】

ASCIIコード(0x00-0x7F)を 0x00-0xFF まで拡張して、半角カナを含めたものです。

アルファベット (Alphabet)、数字 (Numerical digit)、片仮名 (Katakana) の頭文字から「ANKコード」と呼ばれるようになりました。

後に「JIS X 0201」 として正式に定義されました。

「JIS X 0201」の俗称が「ANKコード」ということになります。


ord

ord — 文字列の先頭バイトを、0 から 255 までの値に変換する

$this->lcdPutchar( Ord( $string[$n] ) );

self.lcdPutchar(ord(string[n]))

※python3の場合、全角文字が1文字としてカウントされて、意図した動きでは無かったため、以下のように対応

for ( $n = 0; $n < strlen( $string ); $n ++ ) {
	$this->lcdPutchar( Ord( $string[$n] ) );
}

def lcdPuts(self, string):
    chars = bytearray(string.encode("sjis"))
    for n in range(len(chars)):
        self.lcdPutchar(chars[n])

三項演算子

$d =( $data & ( 0x01 << $i ) ) ? WiringPi::HIGH : WiringPi::LOW;

d = wiringpi.HIGH if (data & (0x10 << i)) else wiringpi.LOW

※trueならHIGH、falseならLOW

loading...