- 記事一覧 >
- ブログ記事
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
になります。
【目次】
define、require、&&、argc、!、strcasecmp、intval、file_get_contents、posix_kill、is_file、pcntl_fork、die、$_SERVER、empty、mkdir、$_REQUEST、opendir readdir、is_file、preg_match、[] 配列追加、rsort、substr、isset、file、array_shift、explode、floatval、連想配列のキーと値追加、trim、json_encode、curl_init、curl_setopt、$_POST、is_array、foreach、引数のデフォルト値、array()、apache_request_headers、strlen、static、$GLOBALS、printf、flush、strpos、header、curl_exec、curl_close、usleep、time、strftime、sprintf、file_put_contents、fseek、ftell、pcntl_signal、無名関数、__construct、protected、for、>>、++、文字コード指定、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
その他、宣伝、誹謗中傷等、当方が不適切と判断した書き込みは、理由の如何を問わず、投稿者に断りなく削除します。
書き込み内容について、一切の責任を負いません。
このコメント機能は、予告無く廃止する可能性があります。ご了承ください。
コメントの削除をご依頼の場合はTwitterのDM等でご連絡ください。