-
前回の記事
「ディレクトリの作成/削除…PHPのmkdirやらrmdirは使いまへんでした 」
の記事への検索がかなり多いのでPHPでディレクトリを作成/削除を簡単に行える関数をより詳しくまとめます。
PHPのmkdir及びrmdirがセーフモードや何らかの原因で動かない場合にはperlへソケット通信で変数を渡してディレクトリの作成/削除を行います。
※perlへのソケット通信は各自セキュリティ対策をより強固にした方が良いと思います。
PHPのディレクトリ作成/削除関数(mkdir及びrmdir)がうまく働かない場合はソケット通信でPerlに変数を渡してディレクトリの作成/削除を行いますが、普通に渡してしまえば外部からの不正アクセスでディレクトリを勝手に改変される恐れがあります。
念のため、セキュリティ対策としてID/PASSを付加して「.htpasswd」という名前のテキストファイルにID=PASSWORDという形式で保存したものを読み取ってID/PASSの認証を行ってからディレクトリの作成/削除を行うようにしてます。
スクリプトの一連動作は下記通りです。
checkDir(操作するディレクトリへのパス, ディレクトリの作成 or 削除モード);
で呼び出しすると操作するディレクトリが配列(複数)かどうかを先ず調べ、配列の場合は一つづつ取り出して処理します。
ディレクトリの処理モードへ行きディレクトリの存在を調べて、ディレクトリの削除モードの場合は削除するディレクトリの中身を調べてファイルなら削除、ディレクトリならもう一度中身を調べてファイルがある場合にはファイルを削除してディレクトリの中身を空にします。
中身が空なのを確認した後、ディレクトリの削除を行います。
ディレクトリの削除を行う際、PHPのrmdirが何らかの原因で働かない場合はperlスクリプトへソケット通信して削除するディレクトリパス、.htpasswdから読み取ったID/PASSを変数として渡し、perl側でID/PASS認証を行った後、ディレクトリを削除します。
操作の失敗は['error']に、Perlスクリプトの応答は['log']にそれぞれ入ります。
ディレクトリ作成/削除スクリプトは下記です。
記事最下部にスクリプト一式ダウンロードリンクを貼ってますので必要な方はお気軽にお試しください。
例文はスクリプト下に記述しておきます。
-
directory.php
<?php
define("PASS_DATA", "log/.htpasswd");
define("PERL_MKDIR", "dirmaker.cgi");
define("PERL_RMDIR", "dirdelete.cgi");
function checkDir($dir, $mode) {
$line = array();
if(is_array($dir)) {
foreach($dir as $dirname) {
$line[] = $mode($dirname);
}
} else {
$line[] = $mode($dir);
}
return $line;
}
function makeDir($dir) {
$log = '';
$error = '';
if(!is_dir($dir)) {
if(@mkdir($dir) == false) {
$data = loadPswd();
$parm = array("dirname" => $dir, "perms" => "707", 'id' => $data['id'], 'pswd' => $data['pswd']);
$results = httpPost(PERL_MKDIR, $parm);
if(is_array($results)) extract($results);
} else {
$log = 'Succeeded! in the making the directory.('.$dir.')';
}
} else {
$log = 'Directory('.$dir.') has already been made.';
}
return array('error' => $error, 'log' => $log);
}
function deleteDir($dirname) {
$log = '';
$error = '';
if(is_dir($dirname)) {
if($dir = @opendir($dirname)) {
while(($file = readdir($dir)) !== false) {
if($file != "." && $file != "..") {
if(filetype($dirname."/".$file) == "dir") {
deleteDir($dirname."/".$file);
} else {
if(@unlink($dirname."/".$file) == false) {
$log = $error = 'It was not possible to delete a file('.$dirname."/".$file.'). Please confirm the permission('.$dirname."/".$file.').';
}
}
}
}
closedir($dir);
if(@rmdir($dirname) == false) {
$data = loadPswd();
$parm = array("dirname" => $dirname, 'id' => $data['id'], 'pswd' => $data['pswd']);
$results = httpPost(PERL_RMDIR, $parm);
if(is_array($results)) extract($results);
} else {
$log = 'Succeeded! in the deletion of the directory.('.$dirname.')';
}
} else {
$log = $error = 'Directory('.$dirname.') is not opened.';
}
} else {
$log = 'Directory('.$dirname.') is not found.';
}
return array('error' => $error, 'log' => $log);
}
function loadPswd() {
$txt = file_get_contents(PASS_DATA);
list($id, $pass) = explode("=", $txt);
return array('id' => $id, 'pswd' => $pass);
}
?>
http_post.php
<?php
function http_post($script, $parm) {
$port = 80;
$host = $_SERVER['HTTP_HOST'];
$url = 'http://'.$host."/".$script;
foreach($parm as $key => $value) {
if($post != "") $post .= "&";
$post .= $key . "=" . $value;
}
$error = false;
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if(!$fp) {
$error = "socket: post error";
} else {
$len = strlen($post);
$postmes = "POST {$url} HTTP/1.1\r\n".
"Host: {$host}\r\nContent-type: application/x-www-form-urlencoded\r\n".
"Content-length: {$len}\r\n\r\n{$post}";
fwrite($fp, $postmes);
$http_line = '';
while(!feof($fp)) {
$http_line .= fgets ($fp,1024);
}
fclose($fp);
}
if($http_line != NULL) {
$lines = explode("\n", rtrim($http_line));
$str = count($lines) - 2;
$value = $lines[count($lines) - 2];
if(ereg("Failed", $value)) {
$error = $value;
} elseif(ereg("Succeeded", $value)) {
$http_line = $value;
} else {
$error = $http_line;
}
}
return array('error' => $error, 'log' => $http_line);
}
?>
dirdelete.cgi(※PHPのrmdirが失敗した場合のディレクトリ削除用Perlスクリプト。)
#!/usr/local/bin/perl
$pass_data = "log/.htpasswd";
$len = read(STDIN, $param, 1024);
$param =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
@plist = split(/&/, $param);
foreach $strdat (@plist) {
($key, $val) = split(/=/, $strdat);
$prm{$key} = $val;
}
if(open(IN, $pass_data)) {
@Lines = <IN>;
close(IN);
foreach $Line (@Lines) {
($id, $pass) = split(/=/, $Line);
}
if($id eq $prm{id}) {
if($pass eq $prm{pswd}) {
if(rmdir $prm{dirname}) {
$result = 'Succeeded! in the deletion of the directory.';
} else {
$result = 'Failed! in the deletion of the directory.';
}
} else {
$result = 'Failed! Password is different.';
}
} else {
$result = 'Failed! ID is different.';
}
} else {
$result = 'Failed! read file.';
}
print "Content-type: text/html\n\n";
print $result.'('.$prm{dirname}.')';
exit;
dirmaker.cgi
#!/usr/local/bin/perl
$pass_data = "log/.htpasswd";
$len = read(STDIN, $param, 1024);
$param =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
@plist = split(/&/, $param);
foreach $strdat (@plist) {
($key, $val) = split(/=/, $strdat);
$prm{$key} = $val;
}
$perms = eval("0$prm{perms}");
$dir = $prm{dirname};
if(open(IN, $pass_data)) {
@Lines = <IN>;
close(IN);
foreach $Line (@Lines) {
($id, $pass) = split(/=/, $Line);
}
if($id eq $prm{id}) {
if($pass eq $prm{pswd}) {
if(mkdir $dir, $perms) {
chmod $perms, $dir;
$result = 'Succeeded! in making the directory.';
} else {
$result = 'Failed! in making the directory.';
}
} else {
$result = 'Failed! Password is different.';
}
} else {
$result = 'Failed! ID is different.';
}
} else {
$result = 'Failed! read file.';
}
print "Content-type: text/html\n\n";
print $result.'('.$prm{dirname}.')';
exit;
上記スクリプトを使ったディレクトリの作成/削除のサンプル例です。
下記の例文通り複数のディレクトリの操作を簡単に行えます。
まずはじめに必要なスクリプトをインクルードします。
<?php
include_once("directory.php");
include_once("http_post.php");
?>
例1.ディクトリの作成。
<?php
$dir = 'sample1';
$results = checkDir($dir, 'makeDir');
foreach($results as $result) {
if($result['error']) {
//エラー時〜
} else {
//成功時〜
}
}
?>
例2.複数ディクトリの作成。
<?php
$dir = array('sample1', 'sample2', 'sample3');
$results = checkDir($dir, 'makeDir');
foreach($results as $result) {
if($result['error']) {
//エラー時〜
} else {
//成功時〜
}
}
?>
例3.ディクトリの削除。
<?php
$dir = 'sample1';
$results = checkDir($dir, 'deleteDir');
foreach($results as $result) {
if($result['error']) {
//エラー時〜
} else {
//成功時〜
}
}
?>
例4.複数ディクトリの削除。
<?php
$dir = array('sample1', 'sample2', 'sample3');
$results = checkDir($dir, 'deleteDir');
foreach($results as $result) {
if($result['error']) {
//エラー時〜
} else {
//成功時〜
}
}
?>
.htpasswd (log/.htpasswd)
id=password
上記スクリプトサンプルの通り、
checkDir(操作するディレクトリへのパス, ディレクトリの作成 or 削除モード);
の第二引数を'makeDir'でディレクトリの作成、'deleteDir'でディレクトリの削除を行います。
perlスクリプトを使う場合は別ディレクトリに格納し、.htaccess等で外部サーバーからのアクセスを遮断させた方が良いと思います。
ソケット通信を行う際はセキュリティを考慮してシステムに組み込んでみてください。
スクリプト一式は下記よりダウンロードできます。
ディレクトリ作成/削除関数スクリプト(directory.zip)のダウンロード
解凍したファイるのdir.phpがメインスクリプトです。
perlスクリプトは各サーバー仕様にあわせてperlへのパス、パーミッションを変更ください。