"""
php_ini - file ``/etc/php.ini``
===============================
This module provides the ``PHPConfig`` class parser, for reading the
options in the ``/etc/php.ini`` file.
Typical content of ``/etc/php.ini`` file is::
[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions =
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = On
max_execution_time = 30
max_input_time = 60
memory_limit = 128M
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
default_mimetype = "text/html"
The class has one attribute ``data`` which is a nested ``dict`` representing sections
of the input INI file. Each section is represented as ``dict`` where keys are name
of options and values are values of those options.
Example:
>>> php_conf["PHP"]["default_mimetype"].value
'text/html'
>>> php_config.data['PHP']['default_mimetype']
'text/html'
>>> php_conf.data['Session']['session.cache_limiter']
'nocache'
>>> php_conf["PHP"]["max_execution_time"].value
30
>>> php_conf["PHP"]["engine"].value # 'On' turns to 'True'
True
>>> php_conf["PHP"]["short_opeh_tag"].value # 'Off' turns to 'False'
False
>>> php_c['PHP']['precision'].value
14
>>> php_conf.get("PHP").get("memory_limit") # '128M' is converted into bytes
134217728
"""
import string
from insights import parser
from insights.core import ConfigParser
from insights.core.filters import add_filter
from insights.parsr.query import eq
from insights.parsr import (Char, EOF, HangingString, InSet,
LeftBracket, Lift, LineEnd, Literal, RightBracket,
Many, Number, OneLineComment, Opt, PosMarker,
QuotedString, skip_none, String, WithIndent, WS, WSChar)
from insights.parsr.query import Directive, Entry, Section
from insights.parsers import ParseException, SkipException
from insights.specs import Specs
# Filter to ensure that the section headings will always be included.
add_filter(Specs.php_ini, "[")
[docs]@parser(Specs.php_ini, continue_on_error=False)
class PHPConf(ConfigParser):
"""
Class for php configuration file.
"""
def parse_doc(self, content):
try:
def to_directive(x):
name, rest = x
rest = [rest] if rest is not None else []
return Directive(name=name.value.strip(), attrs=rest, lineno=name.lineno, src=self)
def to_section(name, rest):
return Section(name=name.value.strip(), children=rest, lineno=name.lineno, src=self)
def apply_defaults(cfg):
if "DEFAULT" not in cfg:
return cfg
defaults = cfg["DEFAULT"]
not_defaults = cfg[~eq("DEFAULT")]
for c in not_defaults:
for d in defaults.grandchildren:
if d.name not in c:
c.children.append(d)
cfg.children = list(not_defaults)
return cfg
def make_bytes(number, char_multiple):
if char_multiple.lower() == 'k':
return number * 2**10
if char_multiple.lower() == 'm':
return number * 2**20
if char_multiple.lower() == 'g':
return number * 2**30
content = "\n".join(content)
header_chars = (set(string.printable) - set(string.whitespace) - set("[]")) | set(" ")
sep_chars = set("=")
key_chars = header_chars - sep_chars
value_chars = set(string.printable) - set("\n\r")
On = Literal("on", True, ignore_case=True)
Off = Literal("off", False, ignore_case=True)
Tru = Literal("true", True, ignore_case=True)
Fals = Literal("false", False, ignore_case=True)
Boolean = ((On | Off | Tru | Fals) & (WSChar | LineEnd)) % "Boolean"
Num = Number & (WSChar | LineEnd)
QuoStr = QuotedString & (WSChar | LineEnd)
# Handle php.ini shorthand notation for memory limits: 1G, 8M, 50K
# https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
MemNum = (Lift(make_bytes) * Number * (Char('K') | Char('M') | Char('G'))) & (WSChar | LineEnd)
LeftEnd = (WS + LeftBracket + WS)
RightEnd = (WS + RightBracket + WS)
Header = (LeftEnd >> PosMarker(String(header_chars)) << RightEnd) % "Header"
Key = WS >> PosMarker(String(key_chars)) << WS
Sep = InSet(sep_chars, "Sep")
Value = WS >> (Boolean | MemNum | Num | QuoStr | HangingString(value_chars))
KVPair = WithIndent(Key + Opt(Sep >> Value)) % "KVPair"
Comment = (WS >> (OneLineComment(";")).map(lambda x: None))
Line = Comment | KVPair.map(to_directive)
Sect = Lift(to_section) * Header * Many(Line).map(skip_none)
Doc = Many(Comment | Sect).map(skip_none)
Top = Doc << WS << EOF
res = Entry(children=Top(content), src=self)
return apply_defaults(res)
except SkipException:
raise
except:
raise ParseException(ParseException("Could not parse content: '{0}'".
format(content)))
[docs] def parse_content(self, content):
super(PHPConf, self).parse_content(content)
dict_all = {}
for section in self.doc:
section_dict = {}
option_names = set(o.name for o in section)
for name in option_names:
options = [str(o.value) for o in section[name]]
section_dict[name] = options[0] if len(options) == 1 else options
dict_all[section.name] = section_dict
self.data = dict_all