2014 lines
63 KiB
PHP
2014 lines
63 KiB
PHP
<?php
|
|
/***************************************************************************************************
|
|
|
|
NAME
|
|
IniFile.phpclass
|
|
|
|
DESCRIPTION
|
|
Ini-File management system.
|
|
|
|
AUTHOR
|
|
Christian Vigh, 01/2010.
|
|
|
|
HISTORY
|
|
[Version : 1.0] [Date : 2010/08/22] [Author : CV]
|
|
Initial release.
|
|
|
|
[Version : 1.0.1] [Date : 2014/11/06] [Author : CV]
|
|
. Fixed some bugs on property names in Save().
|
|
. Added the $keys_by_reference boolean parameter to GetKeys(). When true (the default,
|
|
and also the original behavior), key values are returned by reference. When false,
|
|
a copy is returned.
|
|
|
|
[Version : 1.0.2] [Date : 2014/12/06] [Author : CV]
|
|
. Swapped the $file and $forced parameters of the Save() method.
|
|
|
|
[Version : 1.0.3] [Date : 2014/12/06] [Author : CV]
|
|
. Changed the GetKeys() method to return an empty array if the section does not exist.
|
|
|
|
[Version : 1.0.4] [Date : 2015/03/28] [Author : CV]
|
|
. Added a regex parameter to the GetSections() method.
|
|
. Added a regex parameter to the GetKeys() method.
|
|
|
|
[Version : 1.0.5] [Date : 2015/04/07] [Author : CV]
|
|
. Changed the __Load() method to allow spaces after the ending keyword of a
|
|
multiline entry.
|
|
|
|
[Version : 1.0.6] [Date : 2015/04/08] [Author : CV]
|
|
. Added the GetBooleanKey() function
|
|
|
|
[Version : 1.0.7] [Date : 2015/04/10] [Author : CV]
|
|
. Corrected a bug in the __Load() method that made current line number tracking
|
|
erroneous. Note that displayed error line may differ from the actual one by an
|
|
index of 1.
|
|
. Changed the __SetPosition() method not to modify the current line number, which is
|
|
now completely tracked by the __Load() method.
|
|
|
|
[Version : 1.0.8] [Date : 2015/04/11] [Author : CV]
|
|
. Changed the GetKey() method so that several key aliases can be specified as an
|
|
array for the $key parameter.
|
|
|
|
[Version : 1.0.9] [Date : 2015/06/12] [Author : CV]
|
|
. Changed the __Load() method so that the equal sign is not mandatory for a key with
|
|
an empty value. Thus, the key :
|
|
|
|
[SomeSection]
|
|
somekey
|
|
|
|
becomes equivalent to :
|
|
|
|
[SomeSection]
|
|
somekey =
|
|
|
|
[Version : 1.0.10] [Date : 2015/06/12] [Author : CV]
|
|
. Changed the Load*() and Append*() methods to include an additional parameter,
|
|
$separator (default is '='), to specify the separator string between a key and its
|
|
value.
|
|
. Authorize spaces in key names
|
|
|
|
***************************************************************************************************/
|
|
|
|
// Determine if we run under Windows or Unix
|
|
if ( ! defined ( 'IS_WINDOWS' ) )
|
|
{
|
|
if ( ! strncasecmp ( php_uname ( 's' ), 'windows', 7 ) )
|
|
{
|
|
define ( 'IS_WINDOWS' , 1 ) ;
|
|
define ( 'IS_UNIX' , 0 ) ;
|
|
}
|
|
else
|
|
{
|
|
define ( 'IS_WINDOWS' , 0 ) ;
|
|
define ( 'IS_UNIX' , 1 ) ;
|
|
}
|
|
}
|
|
|
|
|
|
/*===========================================================================================
|
|
|
|
IniFile class -
|
|
.INI file management. This class tries as much as possible to preserve the original
|
|
text formatting including comments when data is loaded from an existing .INI file.
|
|
This means that if you use the Save() method to save .INI file contents, the generated
|
|
file will have exactly the same contents as the original file (except for the
|
|
modifications you made between loading and saving), and comments will be preserved.
|
|
|
|
The differences with a real .INI file are the following :
|
|
- Comments :
|
|
. A mono-line comment can be introduced with the '#', ';' and '//' construct
|
|
. A multi-line comment is enclosed between '/*' and '* /'.
|
|
- You cannot put comments at the end of a section key definitions. Comments are
|
|
considered to be part of the key value.
|
|
- Section key definitions can be multiline. In that case, you have to use the '<<'
|
|
construct as in the following example :
|
|
|
|
mykey =<<
|
|
some text for my key value
|
|
(continued)
|
|
END
|
|
|
|
By default, a multiline key definition ends with a line containing only the word 'END'.
|
|
You can specify however a different termination, after the '<<' construct :
|
|
|
|
mykey =<<KEYEND
|
|
some text for my key value
|
|
(continued)
|
|
KEYEND
|
|
|
|
In both examples, the value of the 'mykey' key will be :
|
|
|
|
"some text for my key value{EOL}(continued)"
|
|
|
|
The "{EOL}" string will be either "\n" on Unix systems or "\r\n" on Windows systems.
|
|
However, if the IniFile object has been created with an existing .INI file
|
|
contents, the EOL string will be that of the initial file.
|
|
|
|
Key definitions can be stored outside any section definition (ie, at the very beginning
|
|
of the .INI file). In that case, they are placed in an section whose name is the
|
|
empty string ("").
|
|
|
|
===========================================================================================*/
|
|
class IniFile
|
|
{
|
|
// Ini file path
|
|
public $File = null ;
|
|
// Reformat on save (align definitions)
|
|
private $AlignmentOption = self::ALIGN_NONE ;
|
|
// Ini file items
|
|
private $Items = array ( ) ;
|
|
// EOL type
|
|
private $CRLF ;
|
|
private $EOL ;
|
|
// Dirty flag
|
|
private $Dirty = false ;
|
|
// Separator between a key and a value
|
|
public $Separator = '=' ;
|
|
|
|
|
|
// Ini entry types
|
|
const INI_COMMENT = "any" ; // Comment or spaces
|
|
const INI_SECTION = "section" ; // Section name
|
|
const INI_ENTRY = "entry" ; // Section entry
|
|
|
|
// Load disposition option
|
|
const LOAD_ANY = 0 ; // The .ini file is loaded if it exists, or will be created
|
|
const LOAD_EXISTING = 1 ; // The .ini file is loaded. If it does not exist, an error will occur
|
|
const LOAD_NEW = 2 ; // The .ini file is recreated, whether existing or not
|
|
|
|
// Key definitions alignment options
|
|
const ALIGN_NONE = 0 ; // No alignment
|
|
const ALIGN_SECTION = 1 ; // Aligns the equal signs within a section only
|
|
const ALIGN_FILE = 2 ; // Aligns the equal signs within the whole file
|
|
|
|
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/****** ******/
|
|
/****** CONSTRUCTOR & MAGIC METHODS ******/
|
|
/****** ******/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
|
|
// Constructor : does nothing. A IniFile object must be created by using the LoadFromxxx methods
|
|
public function __construct ( $separator = '=' )
|
|
{
|
|
// Determine the current EOL string, depending on OS type.
|
|
// This setting may be overriden if .INI file contents coming from a different OS are loaded.
|
|
if ( IS_WINDOWS )
|
|
{
|
|
$this -> CRLF = true ;
|
|
$this -> EOL = "\r\n" ;
|
|
}
|
|
else
|
|
{
|
|
$this -> CRLF = false ;
|
|
$this -> EOL = "\n" ;
|
|
}
|
|
|
|
$this -> Separator = $separator ;
|
|
}
|
|
|
|
|
|
// Conversion to string : returns the contents of the .INI file
|
|
public function __tostring ( )
|
|
{ return ( $this -> AsString ( ) ) ; }
|
|
|
|
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/****** ******/
|
|
/****** PRIVATE FUNCTIONS ******/
|
|
/****** ******/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
|
|
//
|
|
// __AddTextBlock -
|
|
// Adds a text block in the chain of .INI file contents. This can include comments but also,
|
|
// more simply, newline separators between a section name and the next line
|
|
//
|
|
private function __AddTextBlock ( $value )
|
|
{
|
|
if ( strlen ( $value ) )
|
|
$this -> Items [] = array ( 'type' => self::INI_COMMENT, 'value' => $value ) ;
|
|
|
|
return ( "" ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __Compact -
|
|
// Called when elements from the $this -> Items array are unset, to remove empty slots
|
|
//
|
|
private function __Compact ( )
|
|
{
|
|
$this -> Items = array_values ( $this -> Items ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __EOLReplace -
|
|
// Internally, end of line inidicators are stored as "\n", whatever the original .INI file format
|
|
// (Windows or Unix). This function restores the original EOL indicator.
|
|
//
|
|
private function __EOLReplace ( $value )
|
|
{
|
|
if ( $this -> CRLF )
|
|
$value = str_replace ( "\n", "\r\n", $value ) ;
|
|
|
|
return ( $value ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __FindKey -
|
|
// Searches for the specified key in the specified section. Returns the key index in the
|
|
// $this -> Items array, or false if key not found.
|
|
//
|
|
private function __FindKey ( $section, $key )
|
|
{
|
|
$section = $this -> __NormalizeName ( $section ) ;
|
|
$key = $this -> __NormalizeName ( $key ) ;
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
if ( $index === false )
|
|
return ( false ) ;
|
|
|
|
for ( $i = $index + 1 ; $i < count ( $this -> Items ) ; $i ++ )
|
|
{
|
|
$item = $this -> Items [$i] ;
|
|
|
|
if ( $item [ 'type' ] == self::INI_ENTRY &&
|
|
! strcasecmp ( $item [ 'name'], $key ) )
|
|
return ( $i ) ;
|
|
else if ( $item [ 'type' ] == self::INI_SECTION )
|
|
break ;
|
|
}
|
|
|
|
return ( false ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __FindSection -
|
|
// Searches for the specified section. Returns the section index in the $this -> Items array,
|
|
// or false if section not found.
|
|
//
|
|
private function __FindSection ( $name )
|
|
{
|
|
$name = $this -> __NormalizeName ( $name ) ;
|
|
$index = 0 ;
|
|
|
|
foreach ( $this -> Items as $item )
|
|
{
|
|
if ( $item [ 'type' ] == self::INI_SECTION &&
|
|
! strcasecmp ( $item [ 'value'], $name ) )
|
|
return ( $index ) ;
|
|
|
|
$index ++ ;
|
|
}
|
|
|
|
return ( false ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __Load -
|
|
// Does the real job of parsing the input file or string contents.
|
|
// The '$file' parameter is used only when displaying error messages.
|
|
//
|
|
// Notes :
|
|
// . If the input string contains duplicate section names, their contents will be merged
|
|
// . If the input string contains duplicate key names, subsequent key definitions will
|
|
// override the original one.
|
|
//
|
|
private function __Load ( $contents, $file = null )
|
|
{
|
|
if ( ! $file )
|
|
$file = '(string)' ;
|
|
|
|
$key_value_separator = $this -> Separator ;
|
|
|
|
$single_re = '/^
|
|
(?P<name> [^ \t' . $key_value_separator . ']+)
|
|
(
|
|
(?P<sep> \s*' . $key_value_separator . '\s*)
|
|
(?P<value> .*)
|
|
)?
|
|
$/isx' ;
|
|
$multi_re = '/^' .
|
|
'(?P<name> [^' . $key_value_separator . ']+)' .
|
|
'(?P<sep> \s*' . $key_value_separator . '\s* \<\<\<? \s* (?P<word> [^\s]*) .* )' .
|
|
'$/isx' ;
|
|
|
|
// Check if we have a Unix or Windows file
|
|
$crlf = ( strpos ( $contents, "\r\n" ) !== false ) ;
|
|
|
|
if ( $crlf ) // Windows file
|
|
{
|
|
// The input string contains "\r\n" end-of-line characters ; replace them with a single "\n"
|
|
// This is used to simplify input string parsing
|
|
$this -> CRLF = true ;
|
|
$this -> EOL = "\r\n" ;
|
|
$contents = str_replace ( "\r", '', $contents ) ;
|
|
}
|
|
else // Unix file
|
|
{
|
|
$this -> CRLF = false ;
|
|
$this -> EOL = "\n" ;
|
|
}
|
|
|
|
// Some initializations
|
|
$contents_length = strlen ( $contents ) ; // Length of the input string
|
|
$text_value = "" ; // Contains parsed comments and newlines, anything that is not part of a
|
|
// section or key definitions
|
|
$line = 0 ; // Current line and char in line during parsing
|
|
$char = 1 ; // (used only when displaying error messages)
|
|
$section = null ; // Current section name
|
|
$errhead = "" ; // Prefix string used when displaying error messages
|
|
|
|
// Parse input string
|
|
$i_start = 0 ;
|
|
|
|
for ( $i = 0 ; $i < $contents_length ; $i ++ )
|
|
{
|
|
// Get current and next chars
|
|
$ch = substr ( $contents, $i, 1 ) ;
|
|
$chnext = ( $i + 1 < $contents_length ) ? substr ( $contents, $i + 1, 1 ) : null ;
|
|
|
|
// Complain if we have a construct other than '//' or '/*'
|
|
if ( $ch == '/' && $chnext != '*' && $chnext != '/' )
|
|
throw ( new Exception ( "$errhead Comment character '/' not followed by single-line comment character ('/') or multiline comment character (*)." ) ) ;
|
|
|
|
// Single-line comment
|
|
if ( $ch == ';' || $ch == '#' || ! strncmp ( substr ( $contents, $i, 2 ), '//', 2 ) )
|
|
{
|
|
// Locate the end of line
|
|
$end = strpos ( $contents, "\n", $i ) ;
|
|
|
|
// If no end of line, this means that we are on the last line of the file
|
|
if ( $end === false )
|
|
{
|
|
$text_value .= substr ( $contents, $i ) ;
|
|
$i = $contents_length ;
|
|
}
|
|
// Otherwise extract the comment portion and update current char and current index accordingly
|
|
else
|
|
{
|
|
$text_value .= substr ( $contents, $i, $end - $i ) ;
|
|
$i = $end ;
|
|
$ch = substr ( $contents, $i, 1 ) ;
|
|
}
|
|
}
|
|
// Multiline comment (note : nested multiline comments are not allowed)
|
|
else if ( ! strncmp ( substr ( $contents, $i, 2 ), "/*", 2 ) )
|
|
{
|
|
$end = strpos ( $contents, "*/", $i ) ;
|
|
|
|
// no comment end found : complain
|
|
if ( $end === false )
|
|
throw ( new Exception ( "$errhead Unterminated multiline comment." ) ) ;
|
|
// Otherwise extract the comment part. Consecutive comments are catenated
|
|
else
|
|
{
|
|
$str = substr ( $contents, $i, $end - $i + 1 ) ;
|
|
|
|
for ( $j = 1 ; $j < strlen ( $str ) ; $j ++ )
|
|
{
|
|
$sch = $str [$j] ;
|
|
$this -> __SetPosition ( $file, $sch, $line, $char, $errhead ) ;
|
|
}
|
|
|
|
$text_value .= $str ;
|
|
$i = $end + 1 ;
|
|
$ch = substr ( $contents, $i, 1 ) ;
|
|
}
|
|
}
|
|
// Opening bracket : this is a section name
|
|
else if ( $ch == '[' )
|
|
{
|
|
// Add any comments that have been found so far
|
|
$text_value = $this -> __AddTextBlock ( $text_value ) ;
|
|
|
|
// Locate the closing bracket
|
|
$end = strpos ( $contents, ']', $i ) ;
|
|
|
|
// Complain if not found
|
|
if ( $end === false )
|
|
throw ( new Exception ( "$errhead Unfinished section start." ) ) ;
|
|
|
|
// Extract the section name
|
|
$section = trim ( substr ( $contents, $i + 1, $end - $i - 1 ) ) ;
|
|
$section = $this -> __NormalizeName ( $section ) ;
|
|
|
|
// If section does not already exist, append it to the Items array
|
|
if ( $this -> __FindSection ( $section ) === false )
|
|
$this -> Items [] = array ( 'type' => self::INI_SECTION, 'value' => $section ) ;
|
|
|
|
$i = $end + 1 ;
|
|
|
|
// This code is for the case where "[section_name]" represents the last characters of the input string,
|
|
// without a terminating newline
|
|
if ( $i < $contents_length )
|
|
$ch = substr ( $contents, $i, 1 ) ;
|
|
}
|
|
// if we fall here, we are arriving on a key=value definition (if the line starts with spaces, they will be
|
|
// included in the preceding comment block)
|
|
else if ( $ch > ' ' )
|
|
{
|
|
// If $section is null, this means that we have not encountered a section name so far.
|
|
// In that case, we create the unnamed section to store the key definitions that are at the beginning
|
|
// of the input string
|
|
if ( $section === null )
|
|
{
|
|
$section = "" ;
|
|
$this -> Items [] = array ( 'type' => self::INI_SECTION, 'value' => "" ) ;
|
|
}
|
|
|
|
// Regular expressions to match a single-line key definition, or the start of multiline one
|
|
// Add any comments encountered so far
|
|
$text_value = $this -> __AddTextBlock ( $text_value ) ;
|
|
|
|
// Find the end of the key definition line
|
|
// If no newline found, this means that the definition is at the last line of the file and
|
|
// that it is not terminated with a newline character
|
|
$nlpos = strpos ( $contents, "\n", $i ) ;
|
|
|
|
if ( $nlpos === false )
|
|
$nlpos = $contents_length ;
|
|
|
|
// Extract the key definition and update the pointer accordingly
|
|
$entry = substr ( $contents, $i, $nlpos - $i ) ;
|
|
$i = $nlpos ;
|
|
|
|
// Multiline definition
|
|
if ( preg_match ( $multi_re, $entry, $matches ) )
|
|
{
|
|
$name = trim ( $matches [ 'name' ] ) ;
|
|
$separator = $matches [ 'sep' ] ;
|
|
$word = trim ( $matches [ 'word' ] ) ;
|
|
$multiline = true ;
|
|
|
|
$closing = strpos ( $contents, "\n$word", $i ) ;
|
|
|
|
if ( $closing == false ) // Closing delimiter is on end of file, no trailing newline
|
|
throw ( new Exception ( "$errhead Unterminated multiline entry '$name'." ) ) ;
|
|
|
|
// Extract definition and update pointer to current char
|
|
$value = substr ( $contents, $i + 1, $closing - $i - 1 ) ;
|
|
$i += strlen ( $value ) + strlen ( $word ) + 1 ;
|
|
|
|
// Allow for spaces after the ending keyword
|
|
while ( $i < $contents_length && $contents [$i] != "\n" && ctype_space ( $contents [$i] ) )
|
|
$i ++ ;
|
|
|
|
if ( $i < $contents_length ) // Skip ending newline
|
|
$i ++ ;
|
|
}
|
|
// Single-line definition
|
|
else if ( preg_match ( $single_re, $entry, $matches ) )
|
|
{
|
|
$name = $matches [ 'name' ] ;
|
|
$separator = ( isset ( $matches [ 'sep' ] ) ) ? $matches [ 'sep' ] : '=' ;
|
|
$value = ( isset ( $matches [ 'value' ] ) ) ? $matches [ 'value' ] : '' ;
|
|
$word = "" ;
|
|
$multiline = false ;
|
|
}
|
|
// Neither multiline, nor single-line : complain
|
|
else
|
|
{
|
|
$this -> __SetPosition ( $file, '', $line+1, $char, $errhead ) ;
|
|
throw ( new Exception ( "$errhead Invalid entry :\n\t$entry." ) ) ;
|
|
}
|
|
|
|
// Build the new item entry
|
|
$vch = substr ( $value, 0, 1 ) ;
|
|
|
|
if ( $vch == ' ' || $vch == "\t" )
|
|
$value = substr ( $value, 1 ) ;
|
|
|
|
$value = rtrim ( $value ) ;
|
|
|
|
$item = array
|
|
(
|
|
'type' => self::INI_ENTRY,
|
|
'section' => $section,
|
|
'name' => $this -> __NormalizeName ( $name ),
|
|
'separator' => $separator,
|
|
'value' => $value,
|
|
'multiline' => $multiline,
|
|
'word' => $word
|
|
) ;
|
|
|
|
// Check if the key already exists
|
|
$key_index = $this -> __FindKey ( $section, $name ) ;
|
|
|
|
// If yes, replace the original definition with the current one
|
|
if ( $key_index !== false )
|
|
$this -> Items [ $key_index ] = $item ;
|
|
// Otherwise, append it to our list of .INI file items
|
|
else
|
|
$this -> Items [] = $item ;
|
|
|
|
// This code is for the case where the key definition represents the last characters of the input string,
|
|
// without a terminating newline
|
|
if ( $i < $contents_length )
|
|
$ch = $contents [$i] ;
|
|
}
|
|
|
|
// Update current line
|
|
$delta = $i - $i_start ;
|
|
|
|
if ( $delta )
|
|
{
|
|
$line_count = substr_count ( $contents, "\n", $i_start, $delta ) ;
|
|
$line += $line_count ;
|
|
}
|
|
|
|
$i_start = $i ;
|
|
|
|
// All possible .INI file element types have been processed ; update the error message prefix
|
|
if ( $i < $contents_length )
|
|
{
|
|
$text_value .= $ch ;
|
|
$this -> __SetPosition ( $file, $ch, $line, $char, $errhead ) ;
|
|
}
|
|
}
|
|
|
|
// Create a new comment block if remaining comments or end of lines have been found
|
|
$text_value = $this -> __AddTextBlock ( $text_value ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __NormalizeName -
|
|
// Normalizes a section or key name : removes enclosing spaces and duplicate spaces within the name.
|
|
//
|
|
private function __NormalizeName ( $name )
|
|
{
|
|
$name = trim( $name ) ;
|
|
$name = preg_replace ( '/\s+/', ' ', $name ) ;
|
|
|
|
return ( $name ) ;
|
|
}
|
|
|
|
|
|
//
|
|
// __SetPosition -
|
|
// Sets the prefix of the error message to be displayed upon error.
|
|
//
|
|
private function __SetPosition ( $file, $ch, $line, &$char, &$errhead )
|
|
{
|
|
$file = basename ( $file ) ;
|
|
|
|
if ( $ch == "\n" || $ch == '' )
|
|
{
|
|
$line ++ ;
|
|
$char = 1 ;
|
|
}
|
|
else
|
|
$char ++ ;
|
|
|
|
$errhead = "$file: line $line, char $char :" ;
|
|
}
|
|
|
|
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/****** ******/
|
|
/****** PUBLIC FUNCTIONS ******/
|
|
/****** ******/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
/*********************************************************************************************/
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
AlignDefinitions - Aligns the key definitions.
|
|
|
|
PROTOTYPE
|
|
$inifile -> AlignDefinition ( $align_option = null ) ;
|
|
|
|
DESCRIPTION
|
|
Aligns the key definitions within .INI file sections, so that the equal signs before
|
|
the key values remain aligned.
|
|
|
|
PARAMETERS
|
|
$align_option -
|
|
Can be anyone of the following values :
|
|
ALIGN_NONE -
|
|
No alignment is performed, the initial key definitions are left as is.
|
|
|
|
ALIGN_SECTION -
|
|
Equal signs in key definitions are aligned on the same column, but
|
|
only at the section level.
|
|
|
|
ALIGN_FILE -
|
|
Equal signs in key definitions are align on the same column at a file-level.
|
|
|
|
null (default value) -
|
|
The alignment option is taken from the value specified with the
|
|
SetAlignment() method. The default value is ALIGN_NONE.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function AlignDefinitions ( $option = null )
|
|
{
|
|
// Get the actual alignment option
|
|
if ( $option === null )
|
|
$option = $this -> AlignmentOption ;
|
|
|
|
if ( $option == self::ALIGN_NONE )
|
|
return ;
|
|
|
|
// Get the sections list
|
|
$sections = $this -> GetSections ( ) ;
|
|
|
|
// If alignment at the file level, compute the maximum length of a key name
|
|
if ( $option == self::ALIGN_FILE )
|
|
{
|
|
$maxlength = - 1 ;
|
|
$items = $this -> GetAllKeys ( ) ;
|
|
|
|
foreach ( $items as $section => $keys )
|
|
{
|
|
foreach ( $keys as $name => $value )
|
|
{
|
|
$length = strlen ( $name ) ;
|
|
|
|
if ( $maxlength < $length )
|
|
$maxlength = $length ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Loop through each section of the .INI file
|
|
foreach ( $sections as $section )
|
|
{
|
|
$keys = $this -> GetKeys ( $section ) ;
|
|
|
|
// If alignment at the section level, compute the maximum length of a key name for that section
|
|
if ( $option == self::ALIGN_SECTION )
|
|
{
|
|
$maxlength = -1 ;
|
|
|
|
foreach ( $keys as $key => $value )
|
|
{
|
|
$length = strlen ( $key ) ;
|
|
|
|
if ( $length > $maxlength )
|
|
$maxlength = $length ;
|
|
}
|
|
}
|
|
|
|
// Now, for each key in the current section, adjust the value of the 'separator' entry
|
|
// to make sure that all equal signs will be aligned
|
|
foreach ( $keys as $key => $value )
|
|
{
|
|
$index = $this -> __FindKey ( $section, $key ) ;
|
|
$item = &$this -> Items [ $index ] ;
|
|
$sep = preg_replace ( '/\s*=\s*/', ' = ', $item [ 'separator' ] ) ;
|
|
$length = $maxlength - strlen ( $item [ 'name' ] ) ;
|
|
|
|
if ( $length > 0 )
|
|
$sep = str_repeat ( ' ', $length ) . $sep ;
|
|
|
|
$item [ 'separator' ] = $sep ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
AppendFromArray,
|
|
AppendFromFile,
|
|
AppendFromString - Appends .INI definitions
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> AppendFromArray ( $array ) ;
|
|
$status = $inifile -> AppendFromFile ( $file ) ;
|
|
$status = $inifile -> AppendFromString ( $string ) ;
|
|
|
|
DESCRIPTION
|
|
Appends definitions from the specified .INI file contents.
|
|
|
|
PARAMETERS
|
|
$array (array of strings) -
|
|
.INI file definitions. The EOL string is determined by the OS version.
|
|
|
|
$file (string) -
|
|
File to be read.
|
|
|
|
$string (string) -
|
|
String to be read.
|
|
|
|
RETURNS
|
|
True if the operation was successful, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function AppendFromArray ( $array )
|
|
{
|
|
$this -> Dirty = true ;
|
|
return ( $this -> __Load ( implode ( $this -> EOL, $array ), null, $this -> Separator ) ) ;
|
|
}
|
|
|
|
|
|
public function AppendFromFile ( $file )
|
|
{
|
|
global $Application ;
|
|
|
|
|
|
$inifile = $Application -> GetAbsolutePath ( $file ) ;
|
|
|
|
if ( file_exists ( $inifile ) )
|
|
{
|
|
$this -> Dirty = true ;
|
|
return ( $this -> __Load ( file_get_contents ( $file ), $file, $this -> Separator ) ) ;
|
|
}
|
|
else
|
|
return ( false ) ;
|
|
}
|
|
|
|
|
|
public function AppendFromString ( $string )
|
|
{
|
|
$this -> Dirty = true ;
|
|
return ( $this -> __Load ( $string, null, $this -> Separator ) ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
AppendSection - Appends a section to the current .INI file.
|
|
|
|
PROTOTYPE
|
|
$inifile -> AppendSection ( $section, $comment_before = null, $comment_after = null ) ;
|
|
|
|
DESCRIPTION
|
|
Appends a section to the .INI file.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name. If the section already exists, nothing happens.
|
|
|
|
$comment_before, $comment_after (string) -
|
|
Comments to be appended before and after the section name (optional).
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function AppendSection ( $section, $comment_before = null, $comment_after = null )
|
|
{
|
|
$section = $this -> __NormalizeName ( $name ) ;
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
if ( $index !== false )
|
|
return ;
|
|
|
|
if ( $comment_before !== null )
|
|
$this -> __AddTextBlock ( $comment_before . "\n", false ) ;
|
|
|
|
$this -> Items [] = array ( 'type' => self::INI_SECTION, 'value' => $section ) ;
|
|
|
|
$this -> __AddTextBlock ( "\n", false ) ;
|
|
|
|
if ( $comment_after !== null )
|
|
$this -> __AddTextBlock ( $comment_after . "\n", false ) ;
|
|
|
|
$this -> Dirty = true ;
|
|
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
AsString - Returns the .INI file as a string
|
|
|
|
PROTOTYPE
|
|
$text = $inifile -> AsString ( $full = true ) ;
|
|
|
|
DESCRIPTION
|
|
Returns the contents of the .INI file as a string.
|
|
|
|
PARAMETERS
|
|
$full (boolean) -
|
|
If true (the default), the initial .INI file comments will be included in the
|
|
result.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function AsString ( $full = true )
|
|
{
|
|
$result = "" ;
|
|
$this -> AlignDefinitions ( ) ;
|
|
|
|
// Loop through items
|
|
foreach ( $this -> Items as $item )
|
|
{
|
|
switch ( $item [ 'type'] )
|
|
{
|
|
// Comment block or newline separator
|
|
case self::INI_COMMENT :
|
|
if ( ! $full )
|
|
break ;
|
|
|
|
$value = $item [ 'value' ] ;
|
|
|
|
if ( $this -> CRLF )
|
|
$value = $this -> __EOLReplace ( $value ) ;
|
|
|
|
$result .= $value ;
|
|
break ;
|
|
|
|
// Section name
|
|
case self::INI_SECTION :
|
|
if ( $item [ 'value' ] )
|
|
$result .= '[' . $item [ 'value' ] . ']' ;
|
|
break ;
|
|
|
|
// Section entry. Handle multiline and single-line keys
|
|
case self::INI_ENTRY :
|
|
if ( $item [ 'multiline'] )
|
|
{
|
|
$value = $this -> __EOLReplace ( $item [ 'value' ] ) ;
|
|
$result .= $item [ 'name' ] . $item [ 'separator' ] . $this -> EOL .
|
|
$value . $this -> EOL .
|
|
$item [ 'word' ] ;
|
|
}
|
|
else
|
|
$result .= $item [ 'name' ] . $item [ 'separator' ] . $item [ 'value' ] ;
|
|
break ;
|
|
|
|
default :
|
|
throw ( new Exception ( "Unknow entry type '" . $item [ 'type'] . "'." ) ) ;
|
|
}
|
|
}
|
|
|
|
// All done, return
|
|
return ( $result ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
ClearKey - Clears a key value.
|
|
|
|
PROTOTYPE
|
|
$inifile -> ClearKey ( $section, $key ) ;
|
|
|
|
DESCRIPTION
|
|
Clears a key value. This is the equivalent of calling :
|
|
|
|
$inifile -> SetKey ( $section, $key, "" ) ;
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name.
|
|
|
|
$key (string) -
|
|
Key name.
|
|
|
|
RETURN VALUE
|
|
True if the section/key pair exists, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function ClearKey ( $section, $key )
|
|
{
|
|
$index = $this -> __FindKey ( $section, $key ) ;
|
|
|
|
if ( $index !== false )
|
|
{
|
|
$item = &$this -> Items [ $index ] ;
|
|
$item [ 'value' ] = "" ;
|
|
$item [ 'multiline' ] = false ;
|
|
|
|
$this -> Dirty = true ;
|
|
|
|
return ( true ) ;
|
|
}
|
|
else
|
|
return ( false ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
ClearSection - Clears a section contents.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> ClearSection ( $section ) ;
|
|
|
|
DESCRIPTION
|
|
Clears a section contents, without removing the section name from the .INI file.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name. Specify an empty string ("") for the unnamed global section.
|
|
|
|
RETURN VALUE -
|
|
true if the section exists, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function ClearSection ( $section )
|
|
{
|
|
// Find the section
|
|
$index = $this -> __FindSection ( $section ) + 1 ;
|
|
$count = count ( $this -> Items ) ;
|
|
|
|
if ( $index === false )
|
|
return ( false ) ;
|
|
|
|
// Isolate section contents in the Items array
|
|
$start = $index ;
|
|
$end = $count ;
|
|
|
|
for ( $i = $index + 1 ; $i < $count ; $i ++ )
|
|
{
|
|
$item = $this -> Items [$i] ;
|
|
|
|
// End of section is either end of file or before beginning of next section
|
|
if ( $item [ 'type' ] == self::INI_SECTION )
|
|
{
|
|
$end = $i ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
// If previous entry before section end is a comment, then let's say it belongs to the next item
|
|
// so don't clear it
|
|
if ( $this -> Items [ $end - 1 ] [ 'type' ] == self::INI_COMMENT )
|
|
$end -- ;
|
|
|
|
// Remove section contents
|
|
for ( $i = $start ; $i < $end ; $i ++ )
|
|
unset ( $this -> Items [$i] ) ;
|
|
|
|
// Compact the Items array
|
|
$this -> __Compact ( ) ;
|
|
$this -> Dirty = true ;
|
|
|
|
// All done, return
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetAlignment - Get the alignment value.
|
|
|
|
PROTOTYPE
|
|
$align = $inifile -> GetAlignment ( ) ;
|
|
|
|
DESCRIPTION
|
|
Gets the section keys alignment option.
|
|
|
|
RETURN VALUE
|
|
IniFile::ALIGN_NONE -
|
|
No alignment takes place. The .INI file will be written back as is.
|
|
|
|
IniFile::ALIGN_SECTION -
|
|
Individual keys within a section will be aligned according to the longest key
|
|
name in the section.
|
|
|
|
IniFile::ALIGN_FILE -
|
|
Keys will be aligned at the .INI file level.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function GetAlignment ( )
|
|
{
|
|
return ( $this -> AlignmentOption ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetAllKeys - Gets the whole .INI file contents.
|
|
|
|
PROTOTYPE
|
|
$result = $inifile -> GetAllKeys ( ) ;
|
|
|
|
DESCRIPTION
|
|
Returns all the sections and corresponding keys defined in the .INI file.
|
|
|
|
RETURN VALUE
|
|
The function returns an associative array corresponding to the sections defined in
|
|
the .INI file ; the value of each item is itself an associative array whoses keys are
|
|
key names and whose values are references to the actual key value.
|
|
|
|
For example, given the following .INI file :
|
|
|
|
;---------------------------------------------
|
|
Global = 1
|
|
|
|
[General]
|
|
Save = true
|
|
Upload = false
|
|
;---------------------------------------------
|
|
|
|
the function will return :
|
|
|
|
$result = array (
|
|
"" => array ( 'Global' => 1 ),
|
|
"General" => array ( 'Save' => true, 'Upload' => false )
|
|
)
|
|
|
|
NOTES
|
|
Since the key values are references to the actual value, you can directly modify a
|
|
key's contents, as in the following example :
|
|
|
|
$result [ 'General' ][ 'Save' ] = false ;
|
|
|
|
instead of calling :
|
|
|
|
$inifile -> SetKey ( 'General', 'Save', false ) ;
|
|
|
|
Note however that in the first case, multiline values will not be correctly handled,
|
|
so the direct modification of a value should only be used for single-line values.
|
|
If you don't want to bother with single- or multi-line values, simply call the
|
|
SetKey() method.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function GetAllKeys ( )
|
|
{
|
|
$result = array ( ) ;
|
|
$current = array ( ) ;
|
|
$section = "" ;
|
|
|
|
// Loop through items
|
|
foreach ( $this -> Items as $item )
|
|
{
|
|
// When a new section is encountered, add the keys collected so far to the previous section
|
|
if ( $item [ 'type' ] == self::INI_SECTION )
|
|
{
|
|
if ( $item [ 'value' ] != $section )
|
|
{
|
|
$result [ $section ] = $current ;
|
|
$current = array ( ) ;
|
|
$section = $item [ 'value' ] ;
|
|
}
|
|
}
|
|
// Otherwise, in case of a key, simply collect it
|
|
else if ( $item [ 'type'] == self::INI_ENTRY )
|
|
$current [ $item [ 'name' ] ] = &$item [ 'value' ] ;
|
|
}
|
|
|
|
// Don't forget the keys belonging to the very last section in the .INI file
|
|
if ( count ( $current ) )
|
|
$result [ $section ] = $current ;
|
|
|
|
// All done, return
|
|
return ( $result ) ;
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetBooleanKey - Gets a boolean key value.
|
|
|
|
PROTOTYPE
|
|
$value = $inifile -> GetKey ( $section, $key, $default = null ) ;
|
|
|
|
DESCRIPTION
|
|
Gets a boolean key from the specified section.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name.
|
|
|
|
$key (string) -
|
|
Key name.
|
|
|
|
$default (any) -
|
|
Default value if the key does not exist.
|
|
|
|
RETURN VALUE
|
|
The boolean value, or $default if the key does not exist.
|
|
An invalid boolean value will generate an error.
|
|
|
|
NOTES
|
|
The GetxxxKey functions do not return a reference to the underlying value. The SetKey()
|
|
method must be used to modify the value, if needed.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
private static $BooleanValuesTable = array (
|
|
"" => false,
|
|
"on" => true,
|
|
"yes" => true,
|
|
"true" => true,
|
|
"checked" => true,
|
|
"1" => true,
|
|
"off" => false,
|
|
"no" => false,
|
|
"false" => false,
|
|
"unchecked" => false,
|
|
"0" => false
|
|
) ;
|
|
|
|
|
|
private static function BooleanValue ( $value )
|
|
{
|
|
// Trim any whitespace and convert to lowercase
|
|
$value = trim ( strtolower ( $value ) ) ;
|
|
|
|
// If the value is numeric, return either true (non-zero) or false (null value)
|
|
if ( is_numeric ( $value ) )
|
|
return ( ( $value ) ? true : false ) ;
|
|
|
|
// Other cases : loop through the boolean value keywords to retrieve the appropriate boolean constant
|
|
foreach ( self::$BooleanValuesTable as $name => $constant )
|
|
{
|
|
if ( ! strcmp ( $name, $value ) )
|
|
return ( $constant ) ;
|
|
}
|
|
|
|
// Otherwise return false : this means that we failed to interpret the value as a boolean constant
|
|
return ( null ) ;
|
|
}
|
|
|
|
public function GetBooleanKey ( $section, $key, $default = null )
|
|
{
|
|
$result = $default ;
|
|
|
|
$value = $this -> GetKey ( $section, $key, null ) ;
|
|
|
|
if ( $value !== null && ( $bvalue = self::BooleanValue ( $value ) ) !== null )
|
|
$result = $bvalue ;
|
|
else
|
|
throw ( new Exception ( "Invalid boolean value \"$value\" for the \"$key\" value of the [$section] section." ) ) ;
|
|
|
|
return ( $result ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetKey - Gets a key value.
|
|
|
|
PROTOTYPE
|
|
$value = $inifile -> GetKey ( $section, $key, $default = null ) ;
|
|
|
|
DESCRIPTION
|
|
Gets a section key value.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name.
|
|
|
|
$key (string) -
|
|
Key name.
|
|
|
|
$default (any) -
|
|
Default value if the key does not exist.
|
|
|
|
RETURN VALUE
|
|
A reference to the key value, or $default if the key does not exist.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function &GetKey ( $section, $key, $default = null )
|
|
{
|
|
$false = $default ;
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
if ( $index === false )
|
|
return $false ;
|
|
|
|
$keys = ( is_array ( $key ) ) ? $key : array ( $key ) ;
|
|
|
|
foreach ( $keys as $key )
|
|
{
|
|
$key = $this -> __NormalizeName ( $key ) ;
|
|
|
|
for ( $i = $index + 1 ; $i < count ( $this -> Items ) ; $i ++ )
|
|
{
|
|
$item = &$this -> Items [$i] ;
|
|
|
|
if ( $item [ 'type'] == self::INI_ENTRY && ! strcasecmp ( $item [ 'name' ], $key ) )
|
|
{
|
|
return $item [ 'value' ] ;
|
|
}
|
|
else if ( $item [ 'type' ] == self::INI_SECTION )
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return $false ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetKeys - Gets the key list for a given section.
|
|
|
|
PROTOTYPE
|
|
$keys = $inifile -> GetKeys ( $section, $keys_by_reference = true, $regex = null ) ;
|
|
|
|
DESCRIPTION
|
|
Gets the key names/values for the specified section.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name. Specify the empty string for the unnamed global section.
|
|
|
|
$keys_by_reference (boolean) -
|
|
When true, key values are returned by reference rather than by copy.
|
|
|
|
$regex (string) -
|
|
When specified, only the matching keys will be returned. The regular expression
|
|
is to be given without any anchor or options.
|
|
|
|
RETURN VALUE
|
|
An associative array of section key/value pairs.
|
|
The key value is a reference to the actual key value so you can modify it without
|
|
calling the SetKey() method. Note that in this case, the single or multiline state of
|
|
the value will not be correctly handled. If you don't want to bother with single or
|
|
multiline state of a value, use the SetKey() method instead.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function GetKeys ( $section, $keys_by_reference = true, $regex = null )
|
|
{
|
|
// Find the section
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
if ( $index === false )
|
|
return ( array ( ) ) ;
|
|
|
|
$result = array ( ) ;
|
|
|
|
if ( $regex )
|
|
$regex = "/^ $regex $/imsx" ;
|
|
|
|
// Section found, loop through items
|
|
for ( $i = $index + 1 ; $i < count ( $this -> Items ) ; $i ++ )
|
|
{
|
|
$item = &$this -> Items [$i] ;
|
|
|
|
// Collect all keys...
|
|
if ( $item [ 'type' ] == self::INI_ENTRY )
|
|
{
|
|
// If a regex is specified, exclude the non-matching keys
|
|
if ( $regex && ! preg_match ( $regex, $item [ 'name' ], $match ) )
|
|
continue ;
|
|
|
|
// Allow a 'name' item to be specified in the regexp
|
|
if ( isset ( $match [ 'name' ] ) )
|
|
$name = $match [ 'name' ] ;
|
|
else
|
|
$name = $item [ 'name' ] ;
|
|
|
|
if ( $keys_by_reference )
|
|
$resulting_item = &$item [ 'value' ] ;
|
|
else
|
|
$resulting_item = $item [ 'value' ] ;
|
|
|
|
// Collect either a reference to the value or the value itself
|
|
$result [ $name ] = $resulting_item ;
|
|
}
|
|
// And stop if we encounter another section (or the end of file)
|
|
else if ( $item [ 'type' ] == self::INI_SECTION )
|
|
break ;
|
|
}
|
|
|
|
// All done, return
|
|
return ( $result ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
GetSections - Gets the section list.
|
|
|
|
PROTOTYPE
|
|
$sections = $inifile -> GetSections ( $regex = null ) ;
|
|
|
|
DESCRIPTION
|
|
Returns a list of section names defined in the .INI file.
|
|
|
|
PARAMETERS
|
|
$regex (string) -
|
|
If specified, only the section names matching the regular expression will be
|
|
returned.
|
|
Don't specify any anchor in the input string since the regular expression will
|
|
be replaced by the following string :
|
|
|
|
/^ \s* $regex \s* $/imsx
|
|
|
|
RETURN VALUE
|
|
An array of section names. If the global unnamed section contains keys, then this array
|
|
will also have an empty string as element.
|
|
When a regular expression is specified, the returned value is an associative array that
|
|
contains the following entries :
|
|
|
|
- name :
|
|
Full section name.
|
|
|
|
- match :
|
|
Matched regular expression.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function GetSections ( $regex = null )
|
|
{
|
|
$result = array ( ) ;
|
|
|
|
if ( $regex )
|
|
$regex = "/^ \s* $regex \s* $/imsx" ;
|
|
|
|
foreach ( $this -> Items as &$item )
|
|
{
|
|
if ( $item [ 'type' ] == self::INI_SECTION )
|
|
{
|
|
if ( $regex )
|
|
{
|
|
$match = array ( ) ;
|
|
|
|
if ( preg_match ( $regex, $item [ 'value' ], $match ) )
|
|
$result [] = array ( 'name' => $item [ 'value' ], 'match' => $match ) ;
|
|
}
|
|
else
|
|
$result [] = $item [ 'value' ] ;
|
|
}
|
|
}
|
|
|
|
return ( $result ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
InsertSection - Inserts a section before another one.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> InsertSection ( $section, $section_before,
|
|
$comment_before = null,
|
|
$comment_after = null ) ;
|
|
|
|
DESCRIPTION
|
|
Inserts a section before another one.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section to be inserted.
|
|
|
|
$section_before (string) -
|
|
Section before which $section is to be inserted.
|
|
|
|
$comment_before, $comment_after (string) -
|
|
When those parameters are specified, a comment is inserted before and/or
|
|
after the section name.
|
|
|
|
RETURN VALUE
|
|
True when everything is ok, false if an error occurred.
|
|
|
|
NOTES
|
|
You cannot insert a section before the global unnamed section ("").
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function InsertSection ( $section, $section_before, $comment_before = null, $comment_after = null )
|
|
{
|
|
// It is forbidden to insert a section before the global unnamed section
|
|
if ( ! $section_before )
|
|
return ( false ) ;
|
|
|
|
// Find section
|
|
$index = $this -> __FindSection ( $section_before ) ;
|
|
|
|
// Append the section if it does not already exists, then return
|
|
if ( $index === false )
|
|
return ( $this -> AppendSection ( $section, $comment_before, $comment_after ) ) ;
|
|
|
|
// Add a comment before, if any was specified
|
|
if ( $comment_before !== null )
|
|
$this -> __AddTextBlock ( $comment_before . "\n", false ) ;
|
|
|
|
// Insert the section
|
|
$section = $this -> __NormalizeName ( $section ) ;
|
|
$item = array ( 'type' => self::INI_SECTION, 'value' => $section ) ;
|
|
array_slice ( $this -> Items, $index, 0, $item ) ;
|
|
|
|
// Add a comment after, if any
|
|
if ( $comment_after !== null )
|
|
$this -> __AddTextBlock ( $comment_after . "\n", false ) ;
|
|
|
|
// All done, return
|
|
$this -> Dirty = true ;
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
IsDirty - Checks the dirty flag.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> IsDirty ( ) ;
|
|
|
|
DESCRIPTION
|
|
Checks if the dirty flag is set, ie if modifications occurred since the initial
|
|
loading of the .INI file.
|
|
|
|
RETURN VALUE
|
|
True if the dirty flag is set, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function IsDirty ( )
|
|
{ return ( $this -> Dirty ) ; }
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
IsKeyDefined - Checks if a key is defined.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> IsKeyDefined ( $section, $key ) ;
|
|
|
|
DESCRIPTION
|
|
Checks if the specified key is defined in the specified section.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name.
|
|
|
|
$key (string) -
|
|
Key name.
|
|
|
|
RETURN VALUE
|
|
True if the specified section/key pair exists, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function IsKeyDefined ( $section, $key )
|
|
{
|
|
$index = $this -> __FindKey ( $section, $key ) ;
|
|
|
|
if ( $index === false )
|
|
return ( false ) ;
|
|
else
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
IsSectionDefined - Checks if the specified section exists.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> IsSectionDefined ( $section ) ;
|
|
|
|
DESCRIPTION
|
|
Checks if the specified section exists within the .INI file.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section name.
|
|
|
|
RETURN VALUE
|
|
True if the specified section exists, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function IsSectionDefined ( $section )
|
|
{
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
if ( $index === false )
|
|
return ( false ) ;
|
|
else
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
LoadFromArray,
|
|
LoadFromFile,
|
|
LoadFromString - Creates a IniFile object.
|
|
|
|
PROTOTYPE
|
|
$inifile = IniFile::LoadFromArray ( $array, $separator = '=' ) ;
|
|
$inifile = IniFile::LoadFromFile ( $file, $load_option = IniFile::LOAD_ANY, $separator = '=' ) ;
|
|
$inifile = IniFile::LoadFromString ( $string, $separator = '=' ) ;
|
|
|
|
DESCRIPTION
|
|
You can create an empty .INI file object by using the new operator ; however, if you
|
|
already have contents to be loaded, use one of these three static functions. It will
|
|
create a IniFile object, load the contents you specified, and return a reference
|
|
to the object.
|
|
|
|
PARAMETERS
|
|
$array (array of strings) -
|
|
Array of strings containing the .INI file. The EOL string will be determined
|
|
by the current OS ( "\r\n" for Windows, "\n" for Unix).
|
|
|
|
$string (string) -
|
|
.INI file contents. The EOL string will be deduced from the supplied string.
|
|
|
|
$file (string) -
|
|
.INI file whose contents are to be loaded.
|
|
|
|
$load_option (enum) -
|
|
One of the following values :
|
|
- LOAD_ANY :
|
|
The specified file is loaded. If it does not exist, it will be created.
|
|
|
|
- LOAD_NEW :
|
|
The specified file is created. If it already exists, it will be
|
|
overridden.
|
|
|
|
- LOAD_EXISTING :
|
|
The specified file is loaded. If it does not exist, an error message
|
|
will be printed.
|
|
|
|
$separator (string) -
|
|
Separator to be used for separating key names from their values.
|
|
|
|
RETURN VALUE
|
|
Returns the created IniFile object, with the specified contents.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public static function LoadFromArray ( $array, $separator = '=' )
|
|
{
|
|
$object = new IniFile ( $separator ) ;
|
|
$object -> __Load ( implode ( $this -> EOL, $array ) ) ;
|
|
|
|
return ( $object ) ;
|
|
}
|
|
|
|
|
|
public static function LoadFromFile ( $inifile, $load_option = self::LOAD_ANY, $separator = '=' )
|
|
{
|
|
global $Application ;
|
|
|
|
|
|
$load = false ;
|
|
|
|
switch ( $load_option )
|
|
{
|
|
case self::LOAD_ANY :
|
|
if ( file_exists ( $inifile ) )
|
|
{
|
|
$load = true ;
|
|
break ;
|
|
}
|
|
else
|
|
{
|
|
$fp = @fopen ( $inifile, "w" ) ;
|
|
|
|
if ( ! $fp )
|
|
throw ( new Exception ( "The .ini file '$file' could not be created." ) ) ;
|
|
|
|
fclose ( $fp ) ;
|
|
}
|
|
break ;
|
|
|
|
case self::LOAD_NEW :
|
|
$fp = @fopen ( $inifile, "w" ) ;
|
|
|
|
if ( ! $fp )
|
|
throw ( new Exception ( "The .ini file '$inifile' cannot be created." ) ) ;
|
|
|
|
fclose ( $fp ) ;
|
|
break ;
|
|
|
|
case self::LOAD_EXISTING :
|
|
if ( file_exists ( $inifile ) )
|
|
$load = true ;
|
|
else
|
|
throw ( new Exception ( "The .ini file '$inifile' does not exist." ) ) ;
|
|
break ;
|
|
|
|
default :
|
|
throw ( new Exception ( "Invalid value '$load_option' specified for the load option parameter of the IniFile constructor." ) ) ;
|
|
}
|
|
|
|
$object = new IniFile ( $separator ) ;
|
|
$object -> File = $inifile ;
|
|
|
|
if ( $load )
|
|
$object -> __Load ( file_get_contents ( $inifile ), $inifile, $separator ) ;
|
|
|
|
return ( $object ) ;
|
|
}
|
|
|
|
|
|
public static function LoadFromString ( $string, $separator = '=' )
|
|
{
|
|
$object = new IniFile ( $separator ) ;
|
|
$object -> __Load ( $string, null, $separator ) ;
|
|
|
|
return ( $object ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
RemoveKey - Removes a key from a section.
|
|
|
|
PROTOTYPE
|
|
$inifile -> RemoveKey ( $section, $key, $clear_comment_before = true ) ;
|
|
|
|
DESCRIPTION
|
|
Removes the specified key within a section.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Name of the section containing the key to be removed.
|
|
|
|
$key (string) -
|
|
Key to remove.
|
|
|
|
$clear_comment_before (boolean) -
|
|
When true, clears the comment before the specified key, if any.
|
|
|
|
RETURN VALUE
|
|
True if the key exists, false otherwise.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function RemoveKey ( $section, $key, $clear_comment_before = true )
|
|
{
|
|
$index = $this -> __FindKey ( $section, $key ) ;
|
|
|
|
if ( $index !== false )
|
|
{
|
|
unset ( $this -> Items [ $index ] ) ;
|
|
|
|
if ( $clear_comment_before && $index && $this -> Items [ $index - 1 ] [ 'type' ] == self::INI_COMMENT )
|
|
unset ( $this -> Items [ $index - 1 ] ) ;
|
|
|
|
$this -> Dirty = true ;
|
|
$this -> __Compact ( ) ;
|
|
|
|
return ( true ) ;
|
|
}
|
|
else
|
|
return ( false ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
RemoveSection - Removes the specified section.
|
|
|
|
PROTOTYPE
|
|
$inifile -> RemoveSection ( $section, $clear_comment_before ) ;
|
|
|
|
DESCRIPTION
|
|
Removes the specified section, with its contents.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section to be removed. Specify the empty string ("") for the global unnamed
|
|
section.
|
|
|
|
$clear_comment_before (boolean) -
|
|
When true, clears the comment before the specified section, if any.
|
|
|
|
RETURN VALUE
|
|
True if the section exists and has been successfully removed, false otherwise.
|
|
|
|
NOTES
|
|
Unlike the ClearSection() method, the RemoveSection() also removes the section name
|
|
from the .INI file.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function RemoveSection ( $section, $clear_comment_before = true )
|
|
{
|
|
// Find the section start
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
$count = count ( $this -> Items ) ;
|
|
|
|
// Fail if it does not exist
|
|
if ( $index === false )
|
|
return ( false ) ;
|
|
|
|
// Locate the section end, which is either the start of a new section or the end of the .INI file
|
|
$start = $index ;
|
|
$end = $count ;
|
|
|
|
for ( $i = $index + 1 ; $i < $count ; $i ++ )
|
|
{
|
|
$item = $this -> Items [$i] ;
|
|
|
|
if ( $item [ 'type' ] == self::INI_SECTION )
|
|
{
|
|
$end = $i ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
// Check if we need to clear a potential comment before the section
|
|
if ( $clear_comment_before && $start )
|
|
{
|
|
if ( $this -> Items [ $start - 1 ] [ 'type' ] == self::INI_COMMENT )
|
|
{
|
|
$this -> Items [ $start - 1 ] [ 'value' ] = "\n\n" ;
|
|
$start -- ;
|
|
}
|
|
}
|
|
|
|
if ( $this -> Items [ $end - 1 ] [ 'type' ] == self::INI_COMMENT )
|
|
$end -- ;
|
|
|
|
// Remove all section entries, including section name (and comment before)
|
|
for ( $i = $start ; $i < $end ; $i ++ )
|
|
unset ( $this -> Items [$i] ) ;
|
|
|
|
// All done, reorder the Items array and return
|
|
$this -> Dirty = true ;
|
|
$this -> __Compact ( ) ;
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
RenameKey - Renames a key in a section.
|
|
|
|
PROTOTYPE
|
|
$status = $inifile -> RenameKey ( $section, $old, $new ) ;
|
|
|
|
DESCRIPTION
|
|
Renames a key contained in the specified section, from $old to $new.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section containing the key to be renamed. Use the empty string ("") for the
|
|
global unnamed section.
|
|
|
|
$old (string) -
|
|
Key to be renamed.
|
|
|
|
$new (string) -
|
|
New name for the key.
|
|
|
|
RETURN VALUE
|
|
This function returns true if the operation was successful, or false if one of the
|
|
following conditions occurred :
|
|
- The section specified by $section does not exist
|
|
- The key specified by $old does not exist
|
|
- The key specified by $new already exist
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function RenameKey ( $section, $old, $new )
|
|
{
|
|
$old_index = $this -> __FindKey ( $section, $old ) ;
|
|
$new = $this -> __NormalizeName ( $new ) ;
|
|
$new_index = $this -> __FindKey ( $section, $new ) ;
|
|
|
|
if ( $new_index !== false || $old_index === false )
|
|
return ( false ) ;
|
|
|
|
$item = &$this -> Items [ $old_index ] ;
|
|
$item [ 'name' ] = $new ;
|
|
|
|
$this -> Dirty = true ;
|
|
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
RenameSection - Renames a section.
|
|
|
|
PROTOTYPE
|
|
$inifile -> RenameSection ( $old, $new ) ;
|
|
|
|
DESCRIPTION
|
|
Renames a section.
|
|
|
|
PARAMETERS
|
|
$old (string) -
|
|
Section to be renamed.
|
|
|
|
$new (string) -
|
|
New name for the section.
|
|
|
|
RETURN VALUE
|
|
The function returns true if the operation was successful, and false if one of the
|
|
following conditions occurs :
|
|
- The section specified by $old does not exist
|
|
- The section specified by $new already exist
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function RenameSection ( $old, $new )
|
|
{
|
|
$old_index = $this -> __FindSection ( $old ) ;
|
|
$new = $this -> __NormalizeName ( $new ) ;
|
|
$new_index = $this -> __FindSection ( $new ) ;
|
|
|
|
if ( $new_index !== false || $old_index === false )
|
|
return ( false ) ;
|
|
|
|
$item = &$this -> Items [ $old_index ] ;
|
|
$item [ 'value' ] = $new ;
|
|
|
|
$this -> Dirty = true ;
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
Save - Saves the current .INI file
|
|
|
|
PROTOTYPE
|
|
$inifile -> Save ( $forced = false, $file = null ) ;
|
|
|
|
DESCRIPTION
|
|
Saves the contents of the current .INI file object.
|
|
|
|
PARAMETERS
|
|
$forced (boolean) -
|
|
Normally, the .INI file is saved if and only if the dirty flag is set.
|
|
You can override this behavior and perform a forced save whatever the initial
|
|
value of the dirty flag, by setting this parameter to true.
|
|
|
|
$file (string) -
|
|
Output file name. This can be used to save .INI file contents to a different
|
|
file than the original one (when the LoadFromFile() method has been used).
|
|
Note however that an error will occur if :
|
|
- The $file parameter has not been specified
|
|
- The .INI file contents were loaded through the LoadFromArray() or
|
|
LoadFromString() methods
|
|
- The $inifile -> File property has not been set by the caller.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function Save ( $forced = false, $file = null )
|
|
{
|
|
if ( ! $file && ! $this -> File )
|
|
throw ( new Exception ( "IniFile::Save() called, but not file has been specified." ) ) ;
|
|
|
|
if ( ! $this -> Dirty && ! $forced )
|
|
return ;
|
|
|
|
if ( ! $file )
|
|
$file = $this -> File ;
|
|
|
|
file_put_contents ( $file, $this -> AsString ( ) ) ;
|
|
$this -> Dirty = false ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
SetAlignment - Set the alignment value.
|
|
|
|
PROTOTYPE
|
|
$align = $inifile -> SetAlignment ( $alignment ) ;
|
|
|
|
DESCRIPTION
|
|
Sets the section keys alignment option.
|
|
|
|
PARAMETERS
|
|
$alignment (enum) -
|
|
IniFile::ALIGN_NONE -
|
|
No alignment takes place. The .INI file will be written back as is.
|
|
|
|
IniFile::ALIGN_SECTION -
|
|
Individual keys within a section will be aligned according to the longest key
|
|
name in the section.
|
|
|
|
IniFile::ALIGN_FILE -
|
|
Keys will be aligned at the .INI file level.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function SetAlignment ( $alignment )
|
|
{
|
|
$this -> AlignmentOption = $alignment ;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
SetKey - Sets or define a new key value.
|
|
|
|
PROTOTYPE
|
|
$inifile -> SetKey ( $section, $key, $value,
|
|
$comment_before = null, $comment_after = null ) ;
|
|
|
|
DESCRIPTION
|
|
Adds a new section key or changes an existing one.
|
|
|
|
PARAMETERS
|
|
$section (string) -
|
|
Section containing the key to be set.
|
|
|
|
$key (string) -
|
|
Name of the key to be added or changed.
|
|
|
|
$value (string) -
|
|
Key value. A multiline key value will be handled correctly.
|
|
|
|
$comment_before, $comment_after (string) -
|
|
If specified, a comment will be added before and/or after the key.
|
|
|
|
--------------------------------------------------------------------------------------------*/
|
|
public function SetKey ( $section, $key, $value, $comment_before = null, $comment_after = null )
|
|
{
|
|
// Find the section key
|
|
$section = $this -> __NormalizeName ( $section ) ;
|
|
$key = $this -> __NormalizeName ( $key ) ;
|
|
$index = $this -> __FindKey ( $section, $key ) ;
|
|
|
|
// If found, set its value
|
|
if ( $index !== false )
|
|
$this -> Items [ $index ] [ 'value' ] = $value ;
|
|
// Otherwise append it to the section
|
|
else
|
|
{
|
|
// Locate the section
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
|
|
// If the section does not exist, append it to the .INI file
|
|
if ( $index === false )
|
|
{
|
|
$this -> __AddTextBlock ( "\n", false ) ;
|
|
$this -> AppendSection ( $section ) ;
|
|
$index = $this -> __FindSection ( $section ) ;
|
|
}
|
|
|
|
// Replace Windows EOL sequences with Unix ones
|
|
$value = str_replace ( "\r\n", "\n", $value ) ;
|
|
|
|
// Handle the multiline state of the key
|
|
if ( strpos ( $value, "\n" ) !== false )
|
|
{
|
|
$multiline = true ;
|
|
$word = 'END' ;
|
|
}
|
|
else
|
|
{
|
|
$multiline = false ;
|
|
$word = '' ;
|
|
}
|
|
|
|
// Create the item
|
|
$item = array
|
|
(
|
|
'type' => self::INI_ENTRY,
|
|
'section' => $section,
|
|
'name' => $key,
|
|
'separator' => ' = ',
|
|
'value' => $value,
|
|
'multiline' => $multiline,
|
|
'word' => $word
|
|
) ;
|
|
|
|
// Append it to the Items array
|
|
$this -> Items [] = $item ;
|
|
$this -> __AddTextBlock ( "\n", false ) ;
|
|
}
|
|
|
|
// All done, return
|
|
$this -> Dirty = true ;
|
|
return ( true ) ;
|
|
}
|
|
|
|
|
|
}
|