plano de contingência para o erro fopen em php
Pergunta
Eu estou escrevendo um aplicativo PHP que tem um 'painel de controle' que grava um arquivo prefs com determinadas variáveis. Em cada POST
, se o arquivo não existir, ele será criado. Se ela existe, é unlinked
e um novo arquivo é touched
com o mesmo nome do arquivo e novas variáveis. Este arquivo é então incluída em outra página com conteúdo exibe com base nas variáveis ??dentro dela.
$file = "phpsettings.php";
if (!file_exists($file)) {
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
} else {
unlink($file);
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
}
fwrite ($handle, $str);
fclose ($handle);
Esta é uma maneira segura de escrever preferências, desde este arquivo será substituído muitas vezes por dia? O que é uma boa maneira de ambos alertando o usuário deste painel de controle se o arquivo não foi salvo corretamente, e, nesse caso, o que seria um bom plano de contingência para evitar quebrar a página deste arquivo prefs está incluído na falta de definição de uma conjunto padrão de variáveis ??para preencher se !(file_exists)
?
Solução
Se você armazenar suas configurações em uma matriz, você pode serializar ()-los e escrever para um arquivo de texto, em vez de escrever php cru para um arquivo php e incluindo-o.
Se você não está saneantes sua entrada para essas preferências, e dizer $ mypref1 representa o nome de alguém, não há nada impedindo-os de preencher esta no campo de formulário:
\"; echo \"PWNED
e seu resultante PHP vai se tornar
<?php \$pref1 = \"$mypref\"; echo \"PWNED\"; ?>
Assim, em primeiro lugar, armazenar as suas preferências em uma matriz e utilizando serialize () é muito mais seguro:
$prefs = array('mypref1' => 'somethingorother');
$handle = fopen ($file, 'w');
fwrite($handle, serialize($prefs));
fclose($h);
// example code demonstrating unserialization
$prefs2 = unserialize(file_get_contents($file));
var_dump($prefs == $prefs2); // should output "(bool) true"
Em sua pergunta, você também mencionar que se o arquivo existir, ele é desvinculado. Você pode simplesmente truncar-lo para comprimento zero, passando de "w" como o segundo argumento para fopen - você não precisa para excluí-lo manualmente. Isto deve definir o mtime de qualquer maneira, negando a necessidade da chamada ao toque ().
Se os valores que estão sendo gravadas no arquivo são preferências, certamente cada preferência poderia ter um padrão, a menos que haja centenas? array_merge lhe permitirá substituir em uma base per-chave, por isso, se você faz algo parecido com isto:
// array of defaults
$prefs = array(
'mypref1' => 'pants',
'mypref2' => 'socks',
);
if (file_exists($file)) {
// if this fails, an E_NOTICE is raised. are you checking your server error
// logs regularly?
if ($userprefs = unserialize(file_get_contents($file))) {
$prefs = array_merge($prefs, $userprefs);
}
}
Se o problema é que há montes, e você não quer ter que inicializar todos eles, você poderia ter um método get_preference que apenas envolve uma chamada isset para a matriz prefs.
function get_preference($name, &$prefs) {
if (isset($pref[$name]))
return $pref[$name];
return null;
}
var_dump(get_preference('mypref1', $prefs));
Além de todas as perguntas Isto levanta, porém, a realidade é que, com a sua aplicação, no caso improvável de que algo faz dar errado com o fopen, deve ser considerado como uma falha grave de qualquer maneira, e um punhado de usuários é provável que você tem a fazer uso desse recurso vai estar em contato com você muito danado rápido se algo der errado.
Outras dicas
É sempre melhor para armazenar seu estado usuários em uma sessão e só persistem nesse estado quando necessário.
Porque não basta usar os recursos de truncamento de fopen ()? Eu acredito que em vez de "r +", você vai precisar passar "w +" ... Então, se o arquivo existir, ele será truncado, se não você só vai criar um novo arquivo. Assim, o código torna-se:
$file = "phpsettings.php";
$handle = fopen( $file, 'w+' );
$str = "<?php \$pref1 = \"$mypref\"; ?>";
fwrite ($handle, $str);
fclose ($handle);