Feature: Labelprint für Kistenetiketten hinzugefügt

This commit is contained in:
2025-10-27 12:14:44 +01:00
parent 43bc416554
commit 14bae6c9ef
1068 changed files with 229014 additions and 1807 deletions

View File

@@ -1,851 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</value>
</option>
<option name="RIGHT_MARGIN" value="130" />
<CssCodeStyleSettings>
<option name="HEX_COLOR_LOWER_CASE" value="true" />
<option name="HEX_COLOR_SHORT_FORMAT" value="true" />
<option name="KEEP_SINGLE_LINE_BLOCKS" value="true" />
<option name="SPACE_BEFORE_OPENING_BRACE" value="false" />
</CssCodeStyleSettings>
<DB2CodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</DB2CodeStyleSettings>
<DerbyCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</DerbyCodeStyleSettings>
<H2CodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</H2CodeStyleSettings>
<HSQLCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</HSQLCodeStyleSettings>
<HTMLCodeStyleSettings>
<option name="HTML_ATTRIBUTE_WRAP" value="0" />
<option name="HTML_TEXT_WRAP" value="0" />
<option name="HTML_ENFORCE_QUOTES" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="ALIGN_OBJECT_PROPERTIES" value="2" />
<option name="ALIGN_VAR_STATEMENTS" value="1" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="IMPORTS_WRAP" value="1" />
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
</JSCodeStyleSettings>
<MSSQLCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</MSSQLCodeStyleSettings>
<MySQLCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</MySQLCodeStyleSettings>
<OracleCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</OracleCodeStyleSettings>
<PHPCodeStyleSettings>
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
<option name="ALIGN_PHPDOC_COMMENTS" value="true" />
<option name="ALIGN_ASSIGNMENTS" value="true" />
<option name="CONCAT_SPACES" value="false" />
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
<option name="LOWER_CASE_NULL_CONST" value="true" />
<option name="ELSE_IF_STYLE" value="COMBINE" />
<option name="FIELDS_DEFAULT_VISIBILITY" value="protected" />
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
<option name="ALIGN_CLASS_CONSTANTS" value="true" />
<option name="KEEP_BLANK_LINES_AFTER_LBRACE" value="1" />
<option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" />
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
<option name="SPACE_AROUND_ASSIGNMENT_IN_DECLARE" value="true" />
<option name="SPACE_AFTER_COLON_IN_RETURN_TYPE" value="false" />
<option name="PHPDOC_USE_FQCN" value="true" />
<option name="MULTILINE_CHAINED_CALLS_SEMICOLON_ON_NEW_LINE" value="true" />
<option name="PREFER_TEMPLATE_INDENTS" value="true" />
</PHPCodeStyleSettings>
<PostgresCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</PostgresCodeStyleSettings>
<SQLiteCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</SQLiteCodeStyleSettings>
<SqlCodeStyleSettings version="6">
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="SELECT_ALIGN_AS" value="false" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_INDENT_THEN_ELSE" value="true" />
<option name="IMP_IF_THEN_INDENT_END" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
<option name="ALIGN_AS_IN_SELECT_STATEMENT" value="false" />
<option name="NEW_LINE_BEFORE_THEN" value="false" />
<option name="INDENT_SELECT_INTO_CLAUSE" value="true" />
</SqlCodeStyleSettings>
<SybaseCodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
<option name="TYPE_CASE" value="3" />
<option name="CUSTOM_TYPE_CASE" value="3" />
<option name="ALIAS_CASE" value="4" />
<option name="BUILT_IN_CASE" value="0" />
<option name="QUERY_TRUE_INDENT" value="false" />
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
<option name="INSERT_EL_COMMA" value="2" />
<option name="SET_EL_WRAP" value="0" />
<option name="SET_EL_COMMA" value="0" />
<option name="WITH_EL_WRAP" value="0" />
<option name="WITH_EL_COMMA" value="0" />
<option name="SELECT_EL_WRAP" value="3" />
<option name="SELECT_EL_COMMA" value="2" />
<option name="FROM_EL_WRAP" value="2" />
<option name="FROM_EL_COMMA" value="2" />
<option name="FROM_PLACE_ON" value="10" />
<option name="WHERE_EL_WRAP" value="3" />
<option name="WHERE_EL_BOUND" value="2" />
<option name="ORDER_EL_COMMA" value="2" />
<option name="TABLE_OPENING" value="1" />
<option name="TABLE_CONTENT" value="2" />
<option name="TABLE_CLOSING" value="3" />
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
<option name="POST_OPT_WRAP_1" value="true" />
<option name="POST_OPT_ALIGN" value="false" />
<option name="ROUTINE_ARG_COMMA" value="2" />
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
<option name="IMP_DECLARE_EL_WRAP" value="1" />
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
<option name="EXPR_CASE_THEN_WRAP" value="true" />
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
</SybaseCodeStyleSettings>
<XML>
<option name="XML_ATTRIBUTE_WRAP" value="0" />
<option name="XML_TEXT_WRAP" value="0" />
<option name="XML_KEEP_WHITE_SPACES_INSIDE_CDATA" value="true" />
</XML>
<codeStyleSettings language="DB2">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Derby">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="H2">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="HSQLDB">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="130" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="WHILE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Markdown">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="MySQL">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Oracle">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="BLANK_LINES_BEFORE_CLASS_END" value="1" />
<option name="CLASS_BRACE_STYLE" value="1" />
<option name="METHOD_BRACE_STYLE" value="1" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="WHILE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="SPECIAL_ELSE_IF_TREATMENT" value="true" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="PARENTHESES_EXPRESSION_LPAREN_WRAP" value="true" />
<option name="PARENTHESES_EXPRESSION_RPAREN_WRAP" value="true" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<CONST />
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PUBLIC />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PROTECTED />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PRIVATE />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PUBLIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PROTECTED />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PRIVATE />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<CONSTRUCTOR />
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PUBLIC />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PROTECTED />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PRIVATE />
<STATIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PUBLIC />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PROTECTED />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PRIVATE />
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<TRAIT />
</match>
</rule>
</section>
<section>
<rule>
<match>
<INTERFACE />
</match>
</rule>
</section>
<section>
<rule>
<match>
<CLASS />
</match>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="PostgreSQL">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SQL">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SQLite">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Sybase">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TSQL">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,27 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JSAccessibilityCheck" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="MessDetectorValidationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpCSValidationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpComposerExtensionStubsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpInternalEntityUsedInspection" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMissingDocCommentInspection" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="PhpMissingParentCallMagicInspection" enabled="false" level="WARNING" enabled_by_default="false">
<option name="ENABLE_FOR_SLEEP" value="false" />
<option name="ENABLE_FOR_WAKEUP" value="false" />
</inspection_tool>
<inspection_tool class="PhpMultipleClassesDeclarationsInOneFile" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpStatementHasEmptyBodyInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUsageOfSilenceOperatorInspection" enabled="true" level="STFU!" enabled_by_default="true" />
<inspection_tool class="PhpVoidFunctionResultUsedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -13,24 +13,24 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
[![Packagist downloads][downloads-badge]][downloads]
[![Documentation][readthedocs-badge]][readthedocs]
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF&logoColor=fff
[php]: https://www.php.net/supported-versions.php
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?logo=packagist
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?logo=packagist&logoColor=fff
[packagist]: https://packagist.org/packages/chillerlan/php-qrcode
[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-qrcode/ci.yml?branch=v5.0.x&logo=github
[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-qrcode/ci.yml?branch=v5.0.x&logo=github&logoColor=fff
[gh-action]: https://github.com/chillerlan/php-qrcode/actions/workflows/ci.yml?query=branch%3Amain
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode/v5.0.x?logo=codecov
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode/v5.0.x?logo=codecov&logoColor=fff
[coverage]: https://app.codecov.io/gh/chillerlan/php-qrcode/tree/v5.0.x
[codacy-badge]: https://img.shields.io/codacy/grade/edccfc4fe5a34b74b1c53ee03f097b8d/v5.0.x?logo=codacy
[codacy-badge]: https://img.shields.io/codacy/grade/edccfc4fe5a34b74b1c53ee03f097b8d/v5.0.x?logo=codacy&logoColor=fff
[codacy]: https://app.codacy.com/gh/chillerlan/php-qrcode/dashboard?branch=v5.0.x
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode?logo=packagist
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode?logo=packagist&logoColor=fff
[downloads]: https://packagist.org/packages/chillerlan/php-qrcode/stats
[readthedocs-badge]: https://img.shields.io/readthedocs/php-qrcode/v5.0.x?logo=readthedocs
[readthedocs-badge]: https://img.shields.io/readthedocs/php-qrcode/v5.0.x?logo=readthedocs&logoColor=fff
[readthedocs]: https://php-qrcode.readthedocs.io/en/v5.0.x/
## Overview
# Overview
### Features
## Features
- Creation of [Model 2 QR Codes](https://www.qrcode.com/en/codes/model12.html), [Version 1 to 40](https://www.qrcode.com/en/about/version.html)
- [ECC Levels](https://www.qrcode.com/en/about/error_correction.html) L/M/Q/H supported
@@ -52,7 +52,7 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
- QR Code reader (via GD and ImageMagick)
### Requirements
## Requirements
- PHP 7.4+
- [`ext-mbstring`](https://www.php.net/manual/book.mbstring.php)
@@ -65,16 +65,21 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
For the QRCode reader, either `ext-gd` or `ext-imagick` is required!
## Documentation
# Documentation
- The user manual is at https://php-qrcode.readthedocs.io/ ([sources](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs))
- An API documentation created with [phpDocumentor](https://www.phpdoc.org/) can be found at https://chillerlan.github.io/php-qrcode/
- The documentation for the `QROptions` container can be found here: [chillerlan/php-settings-container](https://github.com/chillerlan/php-settings-container#readme)
**Important: Please use the examples from the branch that matches your installed php-qrcode version (
[v4.x](https://github.com/chillerlan/php-qrcode/tree/v4.3.x/examples),
[v5.x](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/examples),
[dev-main](https://github.com/chillerlan/php-qrcode/tree/main/examples)
)!**
## Installation with [composer](https://getcomposer.org)
See [the installation guide](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage-Installation.html) for more info!
See [the installation guide](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage/Installation.html) for more info!
### Terminal
@@ -117,7 +122,7 @@ Also, have a look [in the examples folder](https://github.com/chillerlan/php-qrc
</p>
### Reading QR Codes
## Reading QR Codes
Using the built-in QR Code reader is pretty straight-forward:
@@ -139,30 +144,30 @@ catch(Throwable $e){
```
## Shameless advertising
# Shameless advertising
Hi, please check out some of my other projects that are way cooler than qrcodes!
- [js-qrcode](https://github.com/chillerlan/js-qrcode) - a javascript port of this library
- [php-authenticator](https://github.com/chillerlan/php-authenticator) - a Google Authenticator implementation (see [authenticator example](https://github.com/chillerlan/php-qrcode/blob/v5.0.x/examples/authenticator.php))
- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
- [php-oauth-core](https://github.com/chillerlan/php-oauth-core) - an OAuth 1/2 client library along with a bunch of [providers](https://github.com/chillerlan/php-oauth-providers)
- [php-oauth](https://github.com/chillerlan/php-oauth) - an OAuth 1/2 client library, fully PSR-7/PSR-17/PSR-18 compatible
- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
- [php-tootbot](https://github.com/php-tootbot/tootbot-template) - a Mastodon bot library (see [@dwil](https://github.com/php-tootbot/dwil))
## Disclaimer!
# Disclaimer!
I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
### License notice
## License notice
- Parts of this code are [ported to PHP](https://github.com/codemasher/php-qrcode-decoder) from the [ZXing project](https://github.com/zxing/zxing) and licensed under the [Apache License, Version 2.0](./NOTICE).
- [The documentation](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs) is licensed under the [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/).
### Trademark Notice
## Trademark Notice
The word "QR Code" is a registered trademark of *DENSO WAVE INCORPORATED*<br>
https://www.qrcode.com/en/faq.html#patentH2Title

View File

@@ -1,6 +1,7 @@
{
"$schema": "https://getcomposer.org/schema.json",
"name": "chillerlan/php-qrcode",
"description": "A QR code generator and reader with a user friendly API. PHP 7.4+",
"description": "A QR Code generator and reader with a user-friendly API. PHP 7.4+",
"homepage": "https://github.com/chillerlan/php-qrcode",
"license": [
"MIT", "Apache-2.0"
@@ -32,6 +33,12 @@
"homepage":"https://github.com/chillerlan/php-qrcode/graphs/contributors"
}
],
"funding": [
{
"type": "Ko-Fi",
"url": "https://ko-fi.com/codemasher"
}
],
"support": {
"docs": "https://php-qrcode.readthedocs.io",
"issues": "https://github.com/chillerlan/php-qrcode/issues",
@@ -42,15 +49,18 @@
"require": {
"php": "^7.4 || ^8.0",
"ext-mbstring": "*",
"chillerlan/php-settings-container": "^2.1.4 || ^3.1"
"chillerlan/php-settings-container": "^2.1.6 || ^3.2.1"
},
"require-dev": {
"chillerlan/php-authenticator": "^4.1 || ^5.1",
"phan/phan": "^5.4",
"ext-fileinfo": "*",
"chillerlan/php-authenticator": "^4.3.1 || ^5.2.1",
"phan/phan": "^5.5.1",
"phpcompatibility/php-compatibility": "10.x-dev",
"phpunit/phpunit": "^9.6",
"phpmd/phpmd": "^2.15",
"setasign/fpdf": "^1.8.2",
"squizlabs/php_codesniffer": "^3.8"
"slevomat/coding-standard": "^8.23.0",
"squizlabs/php_codesniffer": "^4.0.0"
},
"suggest": {
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
@@ -59,21 +69,26 @@
},
"autoload": {
"psr-4": {
"chillerlan\\QRCode\\": "src/"
"chillerlan\\QRCode\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"chillerlan\\QRCodeTest\\": "tests/"
"chillerlan\\QRCodeTest\\": "tests"
}
},
"scripts": {
"phpunit": "@php vendor/bin/phpunit",
"phan": "@php vendor/bin/phan"
"phan": "@php vendor/bin/phan",
"phpcs": "@php vendor/bin/phpcs",
"phpmd": "@php vendor/bin/phpmd src text ./phpmd.xml.dist",
"phpunit": "@php vendor/bin/phpunit"
},
"config": {
"lock": false,
"sort-packages": true,
"platform-check": true
"platform-check": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

View File

@@ -38,7 +38,7 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
*
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function __construct($gdImage, SettingsContainerInterface $options = null){
public function __construct($gdImage, ?SettingsContainerInterface $options = null){
/** @noinspection PhpFullyQualifiedNameUsageInspection */
if(
@@ -85,12 +85,12 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
}
/** @inheritDoc */
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring($blob), $options);
}

View File

@@ -35,7 +35,7 @@ final class GenericGFPoly{
* @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
* is not a constant polynomial (that is, it is not the monomial "0")
*/
public function __construct(array $coefficients, int $degree = null){
public function __construct(array $coefficients, ?int $degree = null){
$degree ??= 0;
if(empty($coefficients)){

View File

@@ -28,7 +28,7 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
/**
* IMagickLuminanceSource constructor.
*/
public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
public function __construct(Imagick $imagick, ?SettingsContainerInterface $options = null){
parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
$this->imagick = $imagick;
@@ -63,12 +63,12 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
}
/** @inheritDoc */
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
return new self(new Imagick(self::checkFile($path)), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
$im = new Imagick;
$im->readImageBlob($blob);

View File

@@ -34,7 +34,7 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
/**
*
*/
public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
public function __construct(int $width, int $height, ?SettingsContainerInterface $options = null){
$this->width = $width;
$this->height = $height;
$this->options = ($options ?? new QROptions);
@@ -57,7 +57,10 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
return $this->height;
}
/** @inheritDoc */
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function getRow(int $y):array{
if($y < 0 || $y >= $this->getHeight()){

View File

@@ -77,7 +77,7 @@ final class MaskPattern{
*/
public function __construct(int $maskPattern){
if((0b111 & $maskPattern) !== $maskPattern){
if(($maskPattern & 0b111) !== $maskPattern){
throw new QRCodeException('invalid mask pattern');
}

View File

@@ -11,7 +11,7 @@
namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use function array_flip, ceil, intdiv, str_split;
use function ceil, intdiv, preg_match, strpos;
/**
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -24,16 +24,9 @@ final class AlphaNum extends QRDataModeAbstract{
/**
* ISO/IEC 18004:2000 Table 5
*
* @var int[]
* @var string
*/
private const CHAR_TO_ORD = [
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
'8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
'+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
];
private const CHAR_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
/**
* @inheritDoc
@@ -51,18 +44,7 @@ final class AlphaNum extends QRDataModeAbstract{
* @inheritDoc
*/
public static function validateString(string $string):bool{
if($string === ''){
return false;
}
foreach(str_split($string) as $chr){
if(!isset(self::CHAR_TO_ORD[$chr])){
return false;
}
}
return true;
return (bool)preg_match('/^[A-Z\d %$*+\-.:\/]+$/', $string);
}
/**
@@ -78,12 +60,15 @@ final class AlphaNum extends QRDataModeAbstract{
// encode 2 characters in 11 bits
for($i = 0; ($i + 1) < $len; $i += 2){
$bitBuffer->put((self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[($i + 1)]]), 11);
$bitBuffer->put(
($this->ord($this->data[$i]) * 45 + $this->ord($this->data[($i + 1)])),
11,
);
}
// encode a remaining character in 6 bits
if($i < $len){
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
$bitBuffer->put($this->ord($this->data[$i]), 6);
}
return $this;
@@ -95,19 +80,7 @@ final class AlphaNum extends QRDataModeAbstract{
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$charmap = array_flip(self::CHAR_TO_ORD);
// @todo
$toAlphaNumericChar = function(int $ord) use ($charmap):string{
if(isset($charmap[$ord])){
return $charmap[$ord];
}
throw new QRCodeDataException('invalid character value: '.$ord);
};
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$result = '';
// Read two characters at a time
while($length > 1){
@@ -116,9 +89,9 @@ final class AlphaNum extends QRDataModeAbstract{
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
}
$nextTwoCharsBits = $bitBuffer->read(11);
$result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45));
$result .= $toAlphaNumericChar($nextTwoCharsBits % 45);
$nextTwoCharsBits = $bitBuffer->read(11);
$result .= self::chr(intdiv($nextTwoCharsBits, 45));
$result .= self::chr($nextTwoCharsBits % 45);
$length -= 2;
}
@@ -128,10 +101,36 @@ final class AlphaNum extends QRDataModeAbstract{
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
}
$result .= $toAlphaNumericChar($bitBuffer->read(6));
$result .= self::chr($bitBuffer->read(6));
}
return $result;
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private function ord(string $chr):int{
/** @phan-suppress-next-line PhanParamSuspiciousOrder */
$ord = strpos(self::CHAR_MAP, $chr);
if($ord === false){
throw new QRCodeDataException('invalid character'); // @codeCoverageIgnore
}
return $ord;
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private static function chr(int $ord):string{
if($ord < 0 || $ord > 44){
throw new QRCodeDataException('invalid character code'); // @codeCoverageIgnore
}
return self::CHAR_MAP[$ord];
}
}

View File

@@ -34,6 +34,7 @@ final class ECI extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
* @noinspection PhpMissingParentConstructorInspection
*/
public function __construct(int $encoding){
@@ -107,7 +108,7 @@ final class ECI extends QRDataModeAbstract{
$id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
}
else{
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte)); // @codeCoverageIgnore
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte));// @codeCoverageIgnore
}
return new ECICharset($id);
@@ -128,17 +129,12 @@ final class ECI extends QRDataModeAbstract{
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$eciCharset = self::parseValue($bitBuffer);
$nextMode = $bitBuffer->read(4);
if($nextMode !== Mode::BYTE){
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $nextMode));
}
$data = Byte::decodeSegment($bitBuffer, $versionNumber);
$encoding = $eciCharset->getName();
$data = self::decodeModeSegment($nextMode, $bitBuffer, $versionNumber);
$encoding = $eciCharset->getName();
if($encoding === null){
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// section 6.4.5: it does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
@@ -152,4 +148,18 @@ final class ECI extends QRDataModeAbstract{
return mb_convert_encoding($data, mb_internal_encoding(), $encoding);
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private static function decodeModeSegment(int $mode, BitBuffer $bitBuffer, int $versionNumber):string{
switch(true){
case $mode === Mode::NUMBER: return Number::decodeSegment($bitBuffer, $versionNumber);
case $mode === Mode::ALPHANUM: return AlphaNum::decodeSegment($bitBuffer, $versionNumber);
case $mode === Mode::BYTE: return Byte::decodeSegment($bitBuffer, $versionNumber);
}
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $mode));
}
}

View File

@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use Throwable;
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
/**
* Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
@@ -64,11 +64,15 @@ final class Hanzi extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function convertEncoding(string $string):string{
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
$detected = mb_detect_encoding($string, null, true);
$detected = mb_detect_encoding(
$string,
[mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ'],
true,
);
if($detected === false){
throw new QRCodeDataException('mb_detect_encoding error');
@@ -199,7 +203,7 @@ final class Hanzi extends QRDataModeAbstract{
$length--;
}
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
}
}

View File

@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use Throwable;
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
/**
* Kanji mode: 13-bit double-byte characters from the Shift-JIS character set
@@ -57,11 +57,10 @@ final class Kanji extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function convertEncoding(string $string):string{
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
$detected = mb_detect_encoding($string, null, true);
$detected = mb_detect_encoding($string, [mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004'], true);
if($detected === false){
throw new QRCodeDataException('mb_detect_encoding error');
@@ -185,7 +184,7 @@ final class Kanji extends QRDataModeAbstract{
$length--;
}
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
}
}

View File

@@ -11,7 +11,7 @@
namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use function array_flip, ceil, intdiv, str_split, substr, unpack;
use function ceil, intdiv, substr, unpack;
/**
* Numeric mode: decimal digits 0 to 9
@@ -21,13 +21,6 @@ use function array_flip, ceil, intdiv, str_split, substr, unpack;
*/
final class Number extends QRDataModeAbstract{
/**
* @var int[]
*/
private const NUMBER_TO_ORD = [
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
];
/**
* @inheritDoc
*/
@@ -44,18 +37,7 @@ final class Number extends QRDataModeAbstract{
* @inheritDoc
*/
public static function validateString(string $string):bool{
if($string === ''){
return false;
}
foreach(str_split($string) as $chr){
if(!isset(self::NUMBER_TO_ORD[$chr])){
return false;
}
}
return true;
return (bool)preg_match('/^\d+$/', $string);
}
/**
@@ -95,12 +77,20 @@ final class Number extends QRDataModeAbstract{
/**
* get the code for the given numeric string
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private function parseInt(string $string):int{
$num = 0;
foreach(unpack('C*', $string) as $chr){
$num = ($num * 10 + $chr - 48);
$ords = unpack('C*', $string);
if($ords === false){
throw new QRCodeDataException('unpack() error');
}
foreach($ords as $ord){
$num = ($num * 10 + $ord - 48);
}
return $num;
@@ -112,19 +102,7 @@ final class Number extends QRDataModeAbstract{
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$charmap = array_flip(self::NUMBER_TO_ORD);
// @todo
$toNumericChar = function(int $ord) use ($charmap):string{
if(isset($charmap[$ord])){
return $charmap[$ord];
}
throw new QRCodeDataException('invalid character value: '.$ord);
};
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$result = '';
// Read three digits at a time
while($length >= 3){
@@ -139,9 +117,9 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar(intdiv($threeDigitsBits, 100));
$result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10);
$result .= $toNumericChar($threeDigitsBits % 10);
$result .= intdiv($threeDigitsBits, 100);
$result .= (intdiv($threeDigitsBits, 10) % 10);
$result .= ($threeDigitsBits % 10);
$length -= 3;
}
@@ -158,8 +136,8 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar(intdiv($twoDigitsBits, 10));
$result .= $toNumericChar($twoDigitsBits % 10);
$result .= intdiv($twoDigitsBits, 10);
$result .= ($twoDigitsBits % 10);
}
elseif($length === 1){
// One digit left over to read
@@ -173,7 +151,7 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar($digitBits);
$result .= $digitBits;
}
return $result;

View File

@@ -195,7 +195,7 @@ final class QRData{
// guess the version number within the given range
for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
if($total <= $this->maxBitsForEcc[$version]){
if($total <= ($this->maxBitsForEcc[$version] - 4)){
return new Version($version);
}
}
@@ -207,7 +207,7 @@ final class QRData{
/**
* creates a BitBuffer and writes the string data to it
*
* @throws \chillerlan\QRCode\QRCodeException on data overflow
* @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
*/
private function writeBitBuffer():void{
$MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);

View File

@@ -175,7 +175,7 @@ class QRMatrix{
*
* @return int[][]|bool[][]
*/
public function getMatrix(bool $boolean = null):array{
public function getMatrix(?bool $boolean = null):array{
if($boolean !== true){
return $this->matrix;
@@ -195,7 +195,7 @@ class QRMatrix{
* @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
* @codeCoverageIgnore
*/
public function matrix(bool $boolean = null):array{
public function matrix(?bool $boolean = null):array{
return $this->getMatrix($boolean);
}
@@ -387,7 +387,7 @@ class QRMatrix{
* 7 # 3
* 6 5 4
*/
public function checkNeighbours(int $x, int $y, int $M_TYPE = null):int{
public function checkNeighbours(int $x, int $y, ?int $M_TYPE = null):int{
$bits = 0;
foreach($this::neighbours as $bit => [$ix, $iy]){
@@ -463,6 +463,7 @@ class QRMatrix{
for($c = 0; $c < 3; $c++){
for($i = 0; $i < 8; $i++){
// phpcs:ignore
$this->set( $h[$c][0] , ($h[$c][1] + $i), false, $this::M_SEPARATOR);
$this->set(($v[$c][0] - $i), $v[$c][1] , false, $this::M_SEPARATOR);
}
@@ -552,7 +553,7 @@ class QRMatrix{
*
* ISO/IEC 18004:2000 Section 8.9
*/
public function setFormatInfo(MaskPattern $maskPattern = null):self{
public function setFormatInfo(?MaskPattern $maskPattern = null):self{
$this->maskPattern = $maskPattern;
$bits = 0; // sets all format fields to false (test mode)
@@ -678,7 +679,7 @@ class QRMatrix{
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
$height ??= $width;
// if width and height happen to be negative or 0 (default value), just return - nothing to do

View File

@@ -415,7 +415,7 @@ final class BitMatrix extends QRMatrix{
* @codeCoverageIgnore
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setQuietZone(int $quietZoneSize = null):self{
public function setQuietZone(?int $quietZoneSize = null):self{
throw new QRCodeDataException('not supported');
}
@@ -423,7 +423,7 @@ final class BitMatrix extends QRMatrix{
* @codeCoverageIgnore
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
throw new QRCodeDataException('not supported');
}

View File

@@ -29,6 +29,7 @@ final class Decoder{
private ?EccLevel $eccLevel = null;
private ?MaskPattern $maskPattern = null;
private BitBuffer $bitBuffer;
private Detector $detector;
/**
* Decodes a QR Code represented as a BitMatrix.
@@ -37,7 +38,8 @@ final class Decoder{
* @throws \Throwable|\chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function decode(LuminanceSourceInterface $source):DecoderResult{
$matrix = (new Detector($source))->detect();
$this->detector = new Detector($source);
$matrix = $this->detector->detect();
try{
// clone the BitMatrix to avoid errors in case we run into mirroring
@@ -148,6 +150,7 @@ final class Decoder{
'data' => $result,
'version' => $this->version,
'eccLevel' => $this->eccLevel,
'finderPatterns' => $this->detector->getFinderPatterns(),
'maskPattern' => $this->maskPattern,
'structuredAppendParity' => $parityData,
'structuredAppendSequence' => $symbolSequence,

View File

@@ -20,13 +20,14 @@ use function property_exists;
* applies to 2D barcode formats. For now, it contains the raw bytes obtained
* as well as a String interpretation of those bytes, if applicable.
*
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
* @property string $data
* @property \chillerlan\QRCode\Common\Version $version
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
* @property int $structuredAppendParity
* @property int $structuredAppendSequence
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
* @property string $data
* @property \chillerlan\QRCode\Common\Version $version
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
* @property int $structuredAppendParity
* @property int $structuredAppendSequence
* @property \chillerlan\QRCode\Detector\FinderPattern[] $finderPatterns
*/
final class DecoderResult{
@@ -37,11 +38,13 @@ final class DecoderResult{
private string $data = '';
private int $structuredAppendParity = -1;
private int $structuredAppendSequence = -1;
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
private array $finderPatterns = [];
/**
* DecoderResult constructor.
*/
public function __construct(iterable $properties = null){
public function __construct(?iterable $properties = null){
if(!empty($properties)){

View File

@@ -94,7 +94,7 @@ final class ReedSolomonDecoder{
while($longerBlocksStartAt >= 0){
$numCodewords = count($result[$longerBlocksStartAt][1]);
if($numCodewords == $shorterBlocksTotalCodewords){
if($numCodewords === $shorterBlocksTotalCodewords){
break;
}

View File

@@ -256,7 +256,7 @@ final class AlignmentPatternFinder{
$i++;
}
if($i == $maxI || $stateCount[1] > $maxCount){
if($i === $maxI || $stateCount[1] > $maxCount){
return null;
}
@@ -269,6 +269,7 @@ final class AlignmentPatternFinder{
return null;
}
// phpcs:ignore
if((5 * abs(($stateCount[0] + $stateCount[1] + $stateCount[2]) - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
return null;
}

View File

@@ -25,6 +25,8 @@ use const NAN;
final class Detector{
private BitMatrix $matrix;
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
private array $finderPatterns = [];
/**
* Detector constructor.
@@ -33,11 +35,20 @@ final class Detector{
$this->matrix = (new Binarizer($source))->getBlackMatrix();
}
/**
* @return \chillerlan\QRCode\Detector\FinderPattern[]
*/
public function getFinderPatterns():array{
return $this->finderPatterns;
}
/**
* Detects a QR Code in an image.
*/
public function detect():BitMatrix{
[$bottomLeft, $topLeft, $topRight] = (new FinderPatternFinder($this->matrix))->find();
$this->finderPatterns = (new FinderPatternFinder($this->matrix))->find();
[$bottomLeft, $topLeft, $topRight] = $this->finderPatterns;
$moduleSize = $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
$dimension = $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
@@ -305,11 +316,11 @@ final class Detector{
*
*/
private function createTransform(
FinderPattern $nw,
FinderPattern $ne,
FinderPattern $sw,
int $size,
AlignmentPattern $ap = null
FinderPattern $nw,
FinderPattern $ne,
FinderPattern $sw,
int $size,
?AlignmentPattern $ap = null
):PerspectiveTransform{
$dimMinusThree = ($size - 3.5);

View File

@@ -27,7 +27,7 @@ final class FinderPattern extends ResultPoint{
/**
*
*/
public function __construct(float $posX, float $posY, float $estimatedModuleSize, int $count = null){
public function __construct(float $posX, float $posY, float $estimatedModuleSize, ?int $count = null){
parent::__construct($posX, $posY, $estimatedModuleSize);
$this->count = ($count ?? 1);

View File

@@ -290,11 +290,13 @@ final class FinderPatternFinder{
// Now also count down, right from center
$i = 1;
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[2]++;
$i++;
}
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && !$this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[3]++;
$i++;
@@ -304,6 +306,7 @@ final class FinderPatternFinder{
return false;
}
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[4]++;
$i++;

View File

@@ -152,15 +152,15 @@ final class GridSampler{
// no need to try/catch as QRMatrix::set() will silently discard out of bounds values
# try{
for($x = 0; $x < $max; $x += 2){
// Black(-ish) pixel
$bits->set(
intdiv($x, 2),
$y,
$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
QRMatrix::M_DATA
);
}
for($x = 0; $x < $max; $x += 2){
// Black(-ish) pixel
$bits->set(
intdiv($x, 2),
$y,
$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
QRMatrix::M_DATA
);
}
# }
# catch(\Throwable $aioobe){//ArrayIndexOutOfBoundsException
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting

View File

@@ -152,7 +152,7 @@ final class PerspectiveTransform{
/**
* @return array[] [$xValues, $yValues]
*/
public function transformPoints(array $xValues, array $yValues = null):array{
public function transformPoints(array $xValues, ?array $yValues = null):array{
$max = count($xValues);
if($yValues !== null){ // unused

View File

@@ -95,7 +95,7 @@ class QREps extends QROutputAbstract{
// CMYK
? '%f %f %f %f C'
// RGB
:'%f %f %f R';
: '%f %f %f R';
return sprintf($format, ...$values);
}
@@ -103,7 +103,7 @@ class QREps extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
[$width, $height] = $this->getOutputDimensions();
$eps = [

View File

@@ -110,7 +110,10 @@ class QRFpdf extends QROutputAbstract{
* Initializes an FPDF instance
*/
protected function initFPDF():FPDF{
return new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
$fpdf->AddPage();
return $fpdf;
}
/**
@@ -118,9 +121,8 @@ class QRFpdf extends QROutputAbstract{
*
* @return string|\FPDF
*/
public function dump(string $file = null){
$this->fpdf = $this->initFPDF();
$this->fpdf->AddPage();
public function dump(?string $file = null, ?FPDF $fpdf = null){
$this->fpdf = ($fpdf ?? $this->initFPDF());
if($this::moduleValueIsValid($this->options->bgColor)){
$bgColor = $this->prepareModuleValue($this->options->bgColor);

View File

@@ -179,7 +179,7 @@ class QRGdImage extends QROutputAbstract{
* @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
* @throws \ErrorException
*/
public function dump(string $file = null){
public function dump(?string $file = null){
set_error_handler(function(int $errno, string $errstr):bool{
throw new ErrorException($errstr, $errno);

View File

@@ -112,7 +112,7 @@ class QRImagick extends QROutputAbstract{
*
* @return string|\Imagick
*/
public function dump(string $file = null){
public function dump(?string $file = null){
$this->setBgColor();
$this->imagick = $this->createImage();

View File

@@ -71,7 +71,7 @@ abstract class QRMarkup extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$data = $this->createMarkup($file !== null);
$this->saveToFile($data, $file);

View File

@@ -146,18 +146,18 @@ abstract class QROutputAbstract implements QROutputInterface{
}
/**
* Prepares the value for the given input ()
* Prepares the value for the given input (return value depends on the output class)
*
* @param mixed $value
*
* @return mixed|null return value depends on the output class
* @return mixed|null
*/
abstract protected function prepareModuleValue($value);
/**
* Returns a default value for either dark or light modules
* Returns a default value for either dark or light modules (return value depends on the output class)
*
* @return mixed|null return value depends on the output class
* @return mixed|null
*/
abstract protected function getDefaultModuleValue(bool $isDark);
@@ -188,7 +188,7 @@ abstract class QROutputAbstract implements QROutputInterface{
/**
* Returns a base64 data URI for the given string and mime type
*/
protected function toBase64DataURI(string $data, string $mime = null):string{
protected function toBase64DataURI(string $data, ?string $mime = null):string{
return sprintf('data:%s;base64,%s', ($mime ?? $this::MIME_TYPE), base64_encode($data));
}
@@ -200,7 +200,7 @@ abstract class QROutputAbstract implements QROutputInterface{
*
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
*/
protected function saveToFile(string $data, string $file = null):void{
protected function saveToFile(string $data, ?string $file = null):void{
if($file === null){
return;

View File

@@ -221,6 +221,6 @@ interface QROutputInterface{
*
* @return mixed
*/
public function dump(string $file = null);
public function dump(?string $file = null);
}

View File

@@ -46,7 +46,7 @@ class QRString extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
switch($this->options->outputType){
case QROutputInterface::STRING_TEXT:
@@ -101,7 +101,7 @@ class QRString extends QROutputAbstract{
*
* @codeCoverageIgnore
*/
public static function ansi8(string $str, int $color, bool $background = null):string{
public static function ansi8(string $str, int $color, ?bool $background = null):string{
$color = max(0, min($color, 255));
$background = ($background === true) ? 48 : 38;

View File

@@ -25,9 +25,9 @@ class QRStringJSON extends QROutputAbstract{
* @inheritDoc
* @throws \JsonException
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans);
$data = json_encode($matrix, $this->options->jsonFlags);;
$data = json_encode($matrix, $this->options->jsonFlags);
$this->saveToFile($data, $file);

View File

@@ -43,7 +43,7 @@ class QRStringText extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$lines = [];
$linestart = $this->options->textLineStart;
@@ -66,7 +66,7 @@ class QRStringText extends QROutputAbstract{
*
* @codeCoverageIgnore
*/
public static function ansi8(string $str, int $color, bool $background = null):string{
public static function ansi8(string $str, int $color, ?bool $background = null):string{
$color = max(0, min($color, 255));
$background = ($background === true) ? 48 : 38;

View File

@@ -182,7 +182,7 @@ class QRCode{
*
* PHP8: accept iterable
*/
public function __construct(SettingsContainerInterface $options = null){
public function __construct(?SettingsContainerInterface $options = null){
$this->setOptions(($options ?? new QROptions));
}
@@ -202,9 +202,14 @@ class QRCode{
/**
* Renders a QR Code for the given $data and QROptions, saves $file optionally
*
* Note: it is possible to add several data segments before calling this method with a valid $data string
* which will result in a mixed-mode QR Code with the given parameter as last element.
*
* @see https://github.com/chillerlan/php-qrcode/issues/246
*
* @return mixed
*/
public function render(string $data = null, string $file = null){
public function render(?string $data = null, ?string $file = null){
if($data !== null){
/** @var \chillerlan\QRCode\Data\QRDataModeInterface $dataInterface */
@@ -226,7 +231,7 @@ class QRCode{
*
* @return mixed
*/
public function renderMatrix(QRMatrix $matrix, string $file = null){
public function renderMatrix(QRMatrix $matrix, ?string $file = null){
return $this->initOutputInterface($matrix)->dump($file ?? $this->options->cachefile);
}
@@ -298,7 +303,7 @@ class QRCode{
throw new QRCodeOutputException('invalid output module');
}
if(!in_array(QROutputInterface::class, class_implements($outputInterface))){
if(!in_array(QROutputInterface::class, class_implements($outputInterface), true)){
throw new QRCodeOutputException('output module does not implement QROutputInterface');
}
@@ -457,8 +462,6 @@ class QRCode{
/**
* Reads a QR Code from a given file
*
* @noinspection PhpUndefinedMethodInspection
*/
public function readFromFile(string $path):DecoderResult{
return $this->readFromSource($this->luminanceSourceFQN::fromFile($path, $this->options));
@@ -466,8 +469,6 @@ class QRCode{
/**
* Reads a QR Code from the given data blob
*
* @noinspection PhpUndefinedMethodInspection
*/
public function readFromBlob(string $blob):DecoderResult{
return $this->readFromSource($this->luminanceSourceFQN::fromBlob($blob, $this->options));

View File

@@ -19,23 +19,23 @@ A container class for settings objects - decouple configuration logic from your
[license]: https://github.com/chillerlan/php-settings-container/blob/main/LICENSE
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-settings-container.svg?logo=codecov
[coverage]: https://codecov.io/github/chillerlan/php-settings-container
[codacy-badge]: https://img.shields.io/codacy/grade/bd2467799e2943d2853ce3ebad5af490/v2.x-php7.4?logo=codacy
[codacy]: https://app.codacy.com/gh/chillerlan/php-settings-container/dashboard?branch=v2.x-php7.4
[codacy-badge]: https://img.shields.io/codacy/grade/bd2467799e2943d2853ce3ebad5af490/main?logo=codacy
[codacy]: https://www.codacy.com/gh/chillerlan/php-settings-container/dashboard?branch=main
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-settings-container.svg?logo=packagist
[downloads]: https://packagist.org/packages/chillerlan/php-settings-container/stats
[gh-action-badge]: https://github.com/chillerlan/php-settings-container/workflows/CI/badge.svg
[gh-action]: https://github.com/chillerlan/php-settings-container/actions?query=workflow%3A%22CI%22
[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-settings-container/ci.yml?branch=main&logo=github
[gh-action]: https://github.com/chillerlan/php-settings-container/actions/workflows/ci.yml?query=branch%3Amain
## Documentation
### Installation
**requires [composer](https://getcomposer.org)**
*composer.json* (note: replace `dev-main` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^2.1` - see [releases](https://github.com/chillerlan/php-settings-container/releases) for valid versions)
*composer.json* (note: replace `dev-main` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^3.0` - see [releases](https://github.com/chillerlan/php-settings-container/releases) for valid versions)
```json
{
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.1",
"chillerlan/php-settings-container": "dev-main"
}
}
@@ -45,18 +45,14 @@ Profit!
## Usage
The `SettingsContainerInterface` (wrapped in`SettingsContainerAbstract` ) provides plug-in functionality for immutable object properties and adds some fancy, like loading/saving JSON, arrays etc.
The `SettingsContainerInterface` (wrapped in`SettingsContainerAbstract`) provides plug-in functionality for immutable object properties and adds some fancy, like loading/saving JSON, arrays etc.
It takes an `iterable` as the only constructor argument and calls a method with the trait's name on invocation (`MyTrait::MyTrait()`) for each used trait.
A PHPStan ruleset to exclude errors generated by accessing magic properties on `SettingsContainerInterface` can be found in `rules-magic-access.neon`.
### Simple usage
```php
class MyContainer extends SettingsContainerAbstract{
protected $foo;
protected $bar;
}
```
Typed properties in PHP 7.4+:
```php
class MyContainer extends SettingsContainerAbstract{
protected string $foo;
protected string $bar;
@@ -64,12 +60,12 @@ class MyContainer extends SettingsContainerAbstract{
```
```php
// use it just like a \stdClass
// use it just like a \stdClass (except the properties are fixed)
$container = new MyContainer;
$container->foo = 'what';
$container->bar = 'foo';
// which is equivalent to
// which is equivalent to
$container = new MyContainer(['bar' => 'foo', 'foo' => 'what']);
// ...or try
$container->fromJSON('{"foo": "what", "bar": "foo"}');
@@ -90,37 +86,48 @@ var_dump($container->nope); // -> null
### Advanced usage
```php
// from library 1
trait SomeOptions{
protected $foo;
protected $what;
protected string $foo;
protected string $what;
// this method will be called in SettingsContainerAbstract::construct()
// after the properties have been set
protected function SomeOptions(){
protected function SomeOptions():void{
// just some constructor stuff...
$this->foo = strtoupper($this->foo);
}
/*
* special prefixed magic setters & getters
*/
// this method will be called from __set() when property $what is set
protected function set_what(string $value){
protected function set_what(string $value):void{
$this->what = md5($value);
}
// this method is called on __get() for the property $what
protected function get_what():string{
return 'hash: '.$this->what;
}
}
// from library 2
trait MoreOptions{
protected $bar = 'whatever'; // provide default values
protected string $bar = 'whatever'; // provide default values
}
```
```php
$commonOptions = [
// SomeOptions
'foo' => 'whatever',
'foo' => 'whatever',
// MoreOptions
'bar' => 'nothing',
];
// now plug the several library options together to a single object
// now plug the several library options together to a single object
$container = new class ($commonOptions) extends SettingsContainerAbstract{
use SomeOptions, MoreOptions;
};
@@ -129,27 +136,31 @@ var_dump($container->foo); // -> WHATEVER (constructor ran strtoupper on the val
var_dump($container->bar); // -> nothing
$container->what = 'some value';
var_dump($container->what); // -> md5 hash of "some value"
var_dump($container->what); // -> hash: 5946210c9e93ae37891dfe96c3e39614 (custom getter added "hash: ")
```
### API
#### [`SettingsContainerAbstract`](https://github.com/chillerlan/php-settings-container/blob/main/src/SettingsContainerAbstract.php)
method | return | info
-------- | ---- | -----------
`__construct(iterable $properties = null)` | - | calls `construct()` internally after the properties have been set
(protected) `construct()` | void | calls a method with trait name as replacement constructor for each used trait
`__get(string $property)` | mixed | calls `$this->{'get_'.$property}()` if such a method exists
`__set(string $property, $value)` | void | calls `$this->{'set_'.$property}($value)` if such a method exists
`__isset(string $property)` | bool |
`__unset(string $property)` | void |
`__toString()` | string | a JSON string
`toArray()` | array |
`fromIterable(iterable $properties)` | `SettingsContainerInterface` |
`toJSON(int $jsonOptions = null)` | string | accepts [JSON options constants](http://php.net/manual/json.constants.php)
`fromJSON(string $json)` | `SettingsContainerInterface` |
`jsonSerialize()` | mixed | implements the [`JsonSerializable`](https://www.php.net/manual/en/jsonserializable.jsonserialize.php) interface
| method | return | info |
|--------------------------------------------|------------------------------|---------------------------------------------------------------------------------------------------------------------|
| `__construct(iterable $properties = null)` | - | calls `construct()` internally after the properties have been set |
| (protected) `construct()` | void | calls a method with trait name as replacement constructor for each used trait |
| `__get(string $property)` | mixed | calls `$this->{'get_'.$property}()` if such a method exists |
| `__set(string $property, $value)` | void | calls `$this->{'set_'.$property}($value)` if such a method exists |
| `__isset(string $property)` | bool | |
| `__unset(string $property)` | void | |
| `__toString()` | string | a JSON string |
| `toArray()` | array | |
| `fromIterable(iterable $properties)` | `SettingsContainerInterface` | |
| `toJSON(int $jsonOptions = null)` | string | accepts [JSON options constants](http://php.net/manual/json.constants.php) |
| `fromJSON(string $json)` | `SettingsContainerInterface` | |
| `jsonSerialize()` | mixed | implements the [`JsonSerializable`](https://www.php.net/manual/en/jsonserializable.jsonserialize.php) interface |
| `serialize()` | string | implements the [`Serializable`](https://www.php.net/manual/en/serializable.serialize.php) interface |
| `unserialize(string $data)` | void | implements the [`Serializable`](https://www.php.net/manual/en/serializable.unserialize.php) interface |
| `__serialize()` | array | implements the [`Serializable`](https://www.php.net/manual/en/language.oop5.magic.php#object.serialize) interface |
| `__unserialize(array $data)` | void | implements the [`Serializable`](https://www.php.net/manual/en/language.oop5.magic.php#object.unserialize) interface |
## Disclaimer
This might be either an utterly genius or completely stupid idea - you decide. However, i like it and it works.

View File

@@ -1,12 +1,12 @@
{
"name": "chillerlan/php-settings-container",
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
"description": "A container class for immutable settings objects. Not a DI container.",
"homepage": "https://github.com/chillerlan/php-settings-container",
"license": "MIT",
"type": "library",
"minimum-stability": "stable",
"keywords": [
"php7", "helper", "container", "settings", "configuration"
"helper", "container", "settings", "configuration"
],
"authors": [
{
@@ -20,28 +20,29 @@
"source": "https://github.com/chillerlan/php-settings-container"
},
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.1",
"ext-json": "*"
},
"require-dev": {
"phan/phan": "^5.4",
"phpmd/phpmd": "^2.13",
"phpunit/phpunit": "^9.6",
"phpcsstandards/php_codesniffer": "^3.8"
"phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.10"
},
"autoload": {
"psr-4": {
"chillerlan\\Settings\\": "src/"
"chillerlan\\Settings\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"chillerlan\\SettingsTest\\": "tests/"
"chillerlan\\SettingsTest\\": "tests"
}
},
"scripts": {
"phpunit": "@php vendor/bin/phpunit",
"phan": "@php vendor/bin/phan"
"phpstan": "@php vendor/bin/phpstan"
},
"config": {
"lock": false,

View File

@@ -0,0 +1,4 @@
parameters:
ignoreErrors:
# yes, these are magic
- message: "#^Access to an undefined property chillerlan\\\\Settings\\\\SettingsContainerInterface\\:\\:\\$[\\w]+\\.$#"

View File

@@ -7,20 +7,23 @@
* @copyright 2018 Smiley
* @license MIT
*/
declare(strict_types=1);
namespace chillerlan\Settings;
use ReflectionClass, ReflectionProperty;
use function get_object_vars, json_decode, json_encode, method_exists, property_exists;
use InvalidArgumentException, JsonException, ReflectionClass, ReflectionProperty;
use function array_keys, get_object_vars, is_object, json_decode, json_encode,
json_last_error_msg, method_exists, property_exists, serialize, unserialize;
use const JSON_THROW_ON_ERROR;
abstract class SettingsContainerAbstract implements SettingsContainerInterface{
/**
* SettingsContainerAbstract constructor.
*
* @phpstan-param array<string, mixed> $properties
*/
public function __construct(iterable $properties = null){
public function __construct(iterable|null $properties = null){
if(!empty($properties)){
$this->fromIterable($properties);
@@ -49,7 +52,7 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
/**
* @inheritdoc
*/
public function __get(string $property){
public function __get(string $property):mixed{
if(!property_exists($this, $property) || $this->isPrivate($property)){
return null;
@@ -67,7 +70,7 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
/**
* @inheritdoc
*/
public function __set(string $property, $value):void{
public function __set(string $property, mixed $value):void{
if(!property_exists($this, $property) || $this->isPrivate($property)){
return;
@@ -120,13 +123,19 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
* @inheritdoc
*/
public function toArray():array{
return get_object_vars($this);
$properties = [];
foreach(array_keys(get_object_vars($this)) as $key){
$properties[$key] = $this->__get($key);
}
return $properties;
}
/**
* @inheritdoc
*/
public function fromIterable(iterable $properties):SettingsContainerInterface{
public function fromIterable(iterable $properties):static{
foreach($properties as $key => $value){
$this->__set($key, $value);
@@ -138,14 +147,21 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
/**
* @inheritdoc
*/
public function toJSON(int $jsonOptions = null):string{
return json_encode($this, ($jsonOptions ?? 0));
public function toJSON(int|null $jsonOptions = null):string{
$json = json_encode($this, ($jsonOptions ?? 0));
if($json === false){
throw new JsonException(json_last_error_msg());
}
return $json;
}
/**
* @inheritdoc
*/
public function fromJSON(string $json):SettingsContainerInterface{
public function fromJSON(string $json):static{
/** @phpstan-var array<string, mixed> $data */
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
return $this->fromIterable($data);
@@ -153,11 +169,84 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
/**
* @inheritdoc
* @phan-suppress PhanUndeclaredClassAttribute
* @return array<string, mixed>
*/
#[\ReturnTypeWillChange]
public function jsonSerialize():array{
return $this->toArray();
}
/**
* Returns a serialized string representation of the object in its current state (except static/readonly properties)
*
* @inheritdoc
* @see \chillerlan\Settings\SettingsContainerInterface::toArray()
*/
public function serialize():string{
return serialize($this);
}
/**
* Restores the data (except static/readonly properties) from the given serialized object to the current instance
*
* @inheritdoc
* @see \chillerlan\Settings\SettingsContainerInterface::fromIterable()
*/
public function unserialize(string $data):void{
$obj = unserialize($data);
if($obj === false || !is_object($obj)){
throw new InvalidArgumentException('The given serialized string is invalid');
}
$reflection = new ReflectionClass($obj);
if(!$reflection->isInstance($this)){
throw new InvalidArgumentException('The unserialized object does not match the class of this container');
}
$properties = $reflection->getProperties(~(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_READONLY));
foreach($properties as $reflectionProperty){
$this->{$reflectionProperty->name} = $reflectionProperty->getValue($obj);
}
}
/**
* Returns a serialized string representation of the object in its current state (except static/readonly properties)
*
* @inheritdoc
* @see \chillerlan\Settings\SettingsContainerInterface::toArray()
*/
public function __serialize():array{
$properties = (new ReflectionClass($this))
->getProperties(~(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_READONLY))
;
$data = [];
foreach($properties as $reflectionProperty){
$data[$reflectionProperty->name] = $reflectionProperty->getValue($this);
}
return $data;
}
/**
* Restores the data from the given array to the current instance
*
* @inheritdoc
* @see \chillerlan\Settings\SettingsContainerInterface::fromIterable()
*
* @param array<string, mixed> $data
*/
public function __unserialize(array $data):void{
foreach($data as $key => $value){
$this->{$key} = $value;
}
}
}

View File

@@ -7,30 +7,28 @@
* @copyright 2018 Smiley
* @license MIT
*/
declare(strict_types=1);
namespace chillerlan\Settings;
use JsonSerializable;
use JsonSerializable, Serializable;
/**
* a generic container with magic getter and setter
*/
interface SettingsContainerInterface extends JsonSerializable{
interface SettingsContainerInterface extends JsonSerializable, Serializable{
/**
* Retrieve the value of $property
*
* @return mixed|null
*/
public function __get(string $property);
public function __get(string $property):mixed;
/**
* Set $property to $value while avoiding private and non-existing properties
*
* @param string $property
* @param mixed $value
*/
public function __set(string $property, $value):void;
public function __set(string $property, mixed $value):void;
/**
* Checks if $property is set (aka. not null), excluding private properties
@@ -43,32 +41,46 @@ interface SettingsContainerInterface extends JsonSerializable{
public function __unset(string $property):void;
/**
* @see SettingsContainerInterface::toJSON()
* @see \chillerlan\Settings\SettingsContainerInterface::toJSON()
*/
public function __toString():string;
/**
* Returns an array representation of the settings object
*
* The values will be run through the magic __get(), which may also call custom getters.
*
* @return array<string, mixed>
*/
public function toArray():array;
/**
* Sets properties from a given iterable
*
* The values will be run through the magic __set(), which may also call custom setters.
*
* @phpstan-param array<string, mixed> $properties
*/
public function fromIterable(iterable $properties):SettingsContainerInterface;
public function fromIterable(iterable $properties):static;
/**
* Returns a JSON representation of the settings object
*
* @see \json_encode()
* @see \chillerlan\Settings\SettingsContainerInterface::toArray()
*
* @throws \JsonException
*/
public function toJSON(int $jsonOptions = null):string;
public function toJSON(int|null $jsonOptions = null):string;
/**
* Sets properties from a given JSON string
*
* @see \chillerlan\Settings\SettingsContainerInterface::fromIterable()
*
* @throws \Exception
* @throws \JsonException
*/
public function fromJSON(string $json):SettingsContainerInterface;
public function fromJSON(string $json):static;
}