Source code for insights.parsers.php_ini

"""
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