"""
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.core import ConfigParser
from insights.core.filters import add_filter
from insights.core.exceptions import ParseException, SkipComponent
from insights.core.plugins import parser
from insights.parsr import (Char, EOF, HangingString, InSet, LeftBracket,
Lift, LineEnd, Literal, Many, Number,
OneLineComment, Opt, PosMarker, QuotedString,
RightBracket, String, WithIndent, WS, WSChar,
skip_none)
from insights.parsr.query import Directive, Entry, Section, eq
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 SkipComponent:
raise
except:
raise 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