Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
parent
5406e4a551
commit
34e5ac43d9
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,8 @@
|
|||
1. aktuelle .tar.gz entpacken
|
||||
2. per Browser auf auf Verzeichnis gehen
|
||||
3. setup durchfuehren (trotz Hinweis der Rechte (diese muessen passen) weiter klicken)
|
||||
4. Der Webserver-Pfad sollte dringend auf www gesetzte werden, so
|
||||
dass die Firmen kritischen Dateien auf gar keinen Fall durch eine fehlerhafte
|
||||
.htaccess Datei jemals online stehen (es geht primaer um den Ordner userdata und conf).
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Xentral Installation
|
||||
|
||||
1. aktuelle .tar.gz entpacken in einem Ordner
|
||||
2. per Browser auf auf Verzeichnis gehen
|
||||
3. setup durchfuehren (trotz Hinweis der Rechte (diese muessen passen) weiter klicken)
|
||||
4. Der Webserver-Pfad sollte dringend auf www gesetzte werden, sodass die
|
||||
Firmen kritischen Dateien auf keinen Fall durch eine fehlerhafte .htaccess
|
||||
Datei jemals online stehen (es geht primaer um den Ordner userdata und conf).
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
Diese Software ist urheberrechtlich geschützt!
|
||||
|
||||
Copyright by Xentral ERP Software GmbH (ehemals WaWision GmbH bzw. embedded projects GmbH)
|
||||
|
||||
|
||||
Diese Software ist urheberrechtlich geschützt. Alle aus dem Urheberrecht resultierenden
|
||||
Rechte stehen dem Lizenzgeber zu. Das Urheberrecht umfaßt insbesondere den
|
||||
Programmcode, die Dokumentation, das Erscheinungsbild, die Struktur und Organisation
|
||||
der Programmdateien, den Programmnamen, Logos und andere Darstellungsformen
|
||||
innerhalb der Software.
|
||||
|
||||
Der Lizenznehmer erhält nur das individuelle Nutzungsrecht an der Software. Ein
|
||||
Erwerb von Rechten an der Software selbst ist damit nicht verbunden. Der Lizenzgeber
|
||||
behält sich alle Veröffentlichungs-, Vervielfältigungs-, Bearbeitungs- und Verwertungsrechte
|
||||
an der Software vor.
|
||||
|
||||
Diese Software enthält folgenden Drittkomponenten unter den folgenden Lizenzen:
|
||||
<table class="mkTable">
|
||||
<tr><th>Komponente</th><th>Lizenz</th><th>Quellcode Download<br>(soweit anwendbar)</th></tr>
|
||||
<tr valign="top">
|
||||
<td>phpmailer Version 2.1 (Wed, June 04 2008)</td>
|
||||
<td>[PHPMAILER]</td>
|
||||
<td><a href="">http://phpmailer.codeworxtech.com</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Some icons used in this software are property of Webalys LLC (https://www.webalys.com) and can be used only in the context of this open source project.
|
||||
|
||||
|
||||
|
||||
phpmailer
|
||||
GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
|
||||
http://phpmailer.codeworxtech.com/
|
||||
simple_dom_html
|
||||
Licensed under The MIT License
|
||||
http://sourceforge.net/projects/simplehtmldom/
|
||||
full calendar 1.5.3
|
||||
MIT-LICENSE.txt and GPL-LICENSE.txt respectively
|
||||
WaWision choose MIT license
|
||||
http://arshaw.com/fullcalendar/
|
||||
jqtouch-1.0-beta-2-r109.zip
|
||||
http://code.google.com/p/jqtouch/
|
||||
The MIT License
|
||||
jQuery UI CSS Framework 1.8.6
|
||||
Dual licensed under the MIT or GPL Version 2 licenses
|
||||
WaWision choose MIT license
|
||||
http://jquery.org/license
|
||||
FPDF
|
||||
FPDF is released under a permissive license: there is no usage restriction. You may embed it freely in your application (commercial or not), with or without modifications.
|
||||
http://www.fpdf.org/
|
||||
Math
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://pear.php.net/package/Math_BigInteger
|
||||
phpseclib
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
PHP_Compat /
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
http://php.net/
|
||||
Spreadsheet_Excel_Reader
|
||||
http://www.php.net/license/3_0.txt PHP License 3.0
|
||||
http://www.roxburgh.me.uk
|
||||
Crypt
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
pretty autocomplete
|
||||
COPYING for license information (LGPL)
|
||||
http://www.horde.org/
|
||||
jquery.mobile.min.js
|
||||
The MIT License
|
||||
http://jquerymobile.com/
|
||||
colorpicker
|
||||
COPYING for license information (LGPL).
|
||||
Original Sphere Plugin v0.1/v0.3, Design/Programming by Ulyses, (c) 2007 ColorJack.com, IE fixes by Hamish.
|
||||
grider.js
|
||||
http://www.opensource.org/licenses/mit-license.php
|
||||
Boris Barroso Camberos
|
||||
tinymce
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
moxiecode.com
|
||||
Flot
|
||||
http://code.google.com/p/flot/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
||||
Klarna
|
||||
https://github.com/klarna/php-xmlrpc
|
||||
The Klarna XMLRPC PHP SDK is released under Apache License, Version 2.0
|
||||
Klarna AB (publ). Headquarters: Stockholm
|
||||
|
||||
jQuery tagEditor Plugin
|
||||
MIT License © Pixabay.com
|
||||
https://github.com/Pixabay/jQuery-tagEditor
|
||||
|
||||
ColorExtractor
|
||||
MIT License © Mathieu Lechat
|
||||
https://github.com/thephpleague/color-extractor
|
||||
|
||||
HTML Purifier
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
https://travis-ci.org/ezyang/htmlpurifier
|
||||
|
||||
league/fractal 0.17.0
|
||||
MIT License
|
||||
https://github.com/thephpleague/fractal
|
||||
|
||||
nikic/fast-route v1.3.0
|
||||
BSD-3-Clause
|
||||
https://github.com/nikic/FastRoute
|
||||
|
||||
rakit/validation
|
||||
MIT License
|
||||
https://github.com/rakit/validation
|
||||
|
||||
aura/sqlquery
|
||||
BSD-2-Clause
|
||||
https://github.com/auraphp/Aura.SqlQuery
|
||||
|
||||
raml2html
|
||||
MIT License
|
||||
https://github.com/raml2html/raml2html
|
||||
|
||||
raml2html werk theme
|
||||
MIT License
|
||||
https://github.com/silicann/raml2html-werk-theme
|
||||
|
||||
FPDF Makefont + TTFParser
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software to use, copy, modify, distribute, sublicense, and/or sell copies of the software, and to permit persons to whom the software is furnished to do so.
|
||||
https://github.com/Setasign/FPDF
|
||||
|
||||
Nickersoft/push.js
|
||||
MIT License © Tyler Nickerson
|
||||
https://github.com/Nickersoft/push.js
|
||||
|
||||
sabre/dav
|
||||
BSD-3-Clause
|
||||
https://github.com/sabre-io/dav
|
||||
|
||||
needim/noty - A jQuery Notification Plugin
|
||||
MIT License
|
||||
https://github.com/needim/noty
|
||||
|
||||
swiss-payment-slip/swiss-payment-slip-fpdf
|
||||
MIT License
|
||||
https://github.com/ravage84/SwissPaymentSlipFpdf
|
||||
|
||||
League\Flysystem
|
||||
MIT License
|
||||
https://github.com/thephpleague/flysystem
|
||||
|
||||
hammerjs/hammer.js
|
||||
MIT License © Jorik Tangelder (Eight Media)
|
||||
https://github.com/hammerjs/hammer.js/
|
||||
|
||||
haltu/muuri
|
||||
MIT License © Haltu Oy
|
||||
https://github.com/haltu/muuri
|
||||
|
||||
FitText.js
|
||||
WTFPL License © Dave Rupert
|
||||
https://github.com/davatron5000/FitText.js
|
||||
|
||||
Smarty template engine
|
||||
LGPL-3.0 License
|
||||
https://github.com/smarty-php/smarty
|
||||
|
||||
TCPDF
|
||||
GNU-LGPL v3 © Nicola Asuni - Tecnick.com
|
||||
https://tcpdf.org
|
||||
|
||||
Guzzle, PHP HTTP client
|
||||
MIT License
|
||||
https://github.com/guzzle/guzzle
|
||||
|
||||
League/OAuth1-Client
|
||||
MIT License
|
||||
https://github.com/thephpleague/oauth1-client
|
||||
|
||||
Etsy Provider for League/OAuth1-Client
|
||||
MIT License © Y0lk <gabriel@inkrebit.com>
|
||||
https://github.com/Y0lk/oauth1-etsy
|
||||
|
||||
Lexend Deca Font
|
||||
SIL Open Font License 1.1 © The Lexend Project Authors
|
||||
https://github.com/ThomasJockin/lexend
|
||||
|
||||
Roboto Font
|
||||
Apache License, Version 2.0 © Google Inc.
|
||||
https://github.com/google/roboto
|
||||
|
||||
HOTP/TOTP Token Generator
|
||||
MIT License
|
||||
https://github.com/lfkeitel/php-totp
|
||||
|
||||
phpseclib/phpseclib
|
||||
MIT License
|
||||
https://github.com/phpseclib/phpseclib
|
||||
|
||||
Laminas/Laminas-mail
|
||||
BSD-3-Clause
|
||||
https://github.com/laminas/laminas-mail
|
||||
|
||||
Verwenden Sie die Software nur, wenn Sie eine gültige Lizenz erworben haben!
|
||||
|
||||
Kontakt: Xentral ERP Software GmbH
|
||||
Fuggerstraße 11
|
||||
D-86150 Augsburg
|
||||
Telefon 0821 268 41 0 41
|
||||
Telefax 0821 268 41 0 42
|
||||
E-Mail kontakt@xentral.biz
|
||||
|
|
@ -0,0 +1,709 @@
|
|||
GNU Affero General Public License
|
||||
=================================
|
||||
|
||||
_Version 3, 19 November 2007_
|
||||
_Copyright © 2007 Free Software Foundation, Inc. <<http://fsf.org/>>_
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: **(1)** assert copyright on the software, and **(2)** offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
## TERMS AND CONDITIONS
|
||||
|
||||
### 0. Definitions
|
||||
|
||||
“This License” refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
“Copyright” also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
“The Program” refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as “you”. “Licensees” and
|
||||
“recipients” may be individuals or organizations.
|
||||
|
||||
To “modify” a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a “modified version” of the
|
||||
earlier work or a work “based on” the earlier work.
|
||||
|
||||
A “covered work” means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To “propagate” a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To “convey” a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays “Appropriate Legal Notices”
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that **(1)** displays an appropriate copyright notice, and **(2)**
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
### 1. Source Code
|
||||
|
||||
The “source code” for a work means the preferred form of the work
|
||||
for making modifications to it. “Object code” means any non-source
|
||||
form of a work.
|
||||
|
||||
A “Standard Interface” means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The “System Libraries” of an executable work include anything, other
|
||||
than the work as a whole, that **(a)** is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and **(b)** serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
“Major Component”, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The “Corresponding Source” for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
### 2. Basic Permissions
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
### 4. Conveying Verbatim Copies
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
### 5. Conveying Modified Source Versions
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
* **a)** The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
* **b)** The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section 7.
|
||||
This requirement modifies the requirement in section 4 to
|
||||
“keep intact all notices”.
|
||||
* **c)** You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
* **d)** If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
“aggregate” if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
### 6. Conveying Non-Source Forms
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
* **a)** Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
* **b)** Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either **(1)** a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or **(2)** access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
* **c)** Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
* **d)** Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
* **e)** Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A “User Product” is either **(1)** a “consumer product”, which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or **(2)** anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, “normally used” refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
“Installation Information” for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
### 7. Additional Terms
|
||||
|
||||
“Additional permissions” are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
* **a)** Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
* **b)** Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
* **c)** Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
* **d)** Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
* **e)** Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
* **f)** Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered “further
|
||||
restrictions” within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
### 8. Termination
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated **(a)**
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and **(b)** permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
### 9. Acceptance Not Required for Having Copies
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
### 10. Automatic Licensing of Downstream Recipients
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An “entity transaction” is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
### 11. Patents
|
||||
|
||||
A “contributor” is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's “contributor version”.
|
||||
|
||||
A contributor's “essential patent claims” are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, “control” includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a “patent license” is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To “grant” such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either **(1)** cause the Corresponding Source to be so
|
||||
available, or **(2)** arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or **(3)** arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. “Knowingly relying” means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is “discriminatory” if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license **(a)** in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or **(b)** primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
### 12. No Surrender of Others' Freedom
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
### 13. Remote Network Interaction; Use with the GNU General Public License
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
### 14. Revised Versions of this License
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License “or any later version” applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
### 15. Disclaimer of Warranty
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
### 16. Limitation of Liability
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
### 17. Interpretation of Sections 15 and 16
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
## How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the “copyright” line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a “Source” link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a “copyright disclaimer” for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<<http://www.gnu.org/licenses/>>.
|
||||
|
||||
|
||||
## Plugins/Bibliotheken
|
||||
|
||||
phpmailer
|
||||
GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
|
||||
http://phpmailer.codeworxtech.com/
|
||||
simple_dom_html
|
||||
Licensed under The MIT License
|
||||
http://sourceforge.net/projects/simplehtmldom/
|
||||
full calendar 1.5.3
|
||||
MIT-LICENSE.txt and GPL-LICENSE.txt respectively
|
||||
http://arshaw.com/fullcalendar/
|
||||
jqtouch-1.0-beta-2-r109.zip
|
||||
http://code.google.com/p/jqtouch/
|
||||
The MIT License
|
||||
jQuery UI CSS Framework 1.8.6
|
||||
Dual licensed under the MIT or GPL Version 2 licenses
|
||||
http://jquery.org/license
|
||||
FPDF
|
||||
FPDF is released under a permissive license: there is no usage restriction. You may embed it freely in your application (commercial or not), with or without modifications.
|
||||
http://www.fpdf.org/
|
||||
Math
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://pear.php.net/package/Math_BigInteger
|
||||
phpseclib
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
PHP_Compat /
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
http://php.net/
|
||||
Spreadsheet_Excel_Reader
|
||||
http://www.php.net/license/3_0.txt PHP License 3.0
|
||||
http://www.roxburgh.me.uk
|
||||
BarcodePHP
|
||||
http://www.barcodephp.com
|
||||
On this page, you can download free software for non-commercial use!!!
|
||||
Crypt
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
pretty autocomplete
|
||||
COPYING for license information (LGPL)
|
||||
http://www.horde.org/
|
||||
jquery.mobile.min.js
|
||||
The MIT License
|
||||
http://jquerymobile.com/
|
||||
colorpicker
|
||||
COPYING for license information (LGPL).
|
||||
Original Sphere Plugin v0.1/v0.3, Design/Programming by Ulyses, (c) 2007 ColorJack.com, IE fixes by Hamish.
|
||||
grider.js
|
||||
http://www.opensource.org/licenses/mit-license.php
|
||||
Boris Barroso Camberos
|
||||
tinymce
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
moxiecode.com
|
||||
Flot
|
||||
http://code.google.com/p/flot/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
Diese Software ist urheberrechtlich geschützt!
|
||||
|
||||
Copyright by WaWision GmbH.
|
||||
|
||||
Diese Software ist urheberrechtlich geschützt. Alle aus dem Urheberrecht resultierenden
|
||||
Rechte stehen dem Lizenzgeber zu. Das Urheberrecht umfaßt insbesondere den
|
||||
Programmcode, die Dokumentation, das Erscheinungsbild, die Struktur und Organisation
|
||||
der Programmdateien, den Programmnamen, Logos und andere Darstellungsformen
|
||||
innerhalb der Software.
|
||||
|
||||
Der Lizenznehmer erhält nur das individuelle Nutzungsrecht an der Software. Ein
|
||||
Erwerb von Rechten an der Software selbst ist damit nicht verbunden. Der Lizenzgeber
|
||||
behält sich alle Veröffentlichungs-, Vervielfältigungs-, Bearbeitungs- und Verwertungsrechte
|
||||
an der Software vor.
|
||||
|
||||
Die folgenden Software-Produkte unterliegen aufgrund von selbständigen Bestandteile oder
|
||||
entsprechenden Open-Source Lizenzen:
|
||||
|
||||
Some icons used in this software are property of Webalys LLC (https://www.webalys.com) and can be used only in the context of this open source project.
|
||||
|
||||
|
||||
phpmailer
|
||||
GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
|
||||
http://phpmailer.codeworxtech.com/
|
||||
simple_dom_html
|
||||
Licensed under The MIT License
|
||||
http://sourceforge.net/projects/simplehtmldom/
|
||||
full calendar 1.5.3
|
||||
MIT-LICENSE.txt and GPL-LICENSE.txt respectively
|
||||
http://arshaw.com/fullcalendar/
|
||||
jqtouch-1.0-beta-2-r109.zip
|
||||
http://code.google.com/p/jqtouch/
|
||||
The MIT License
|
||||
jQuery UI CSS Framework 1.8.6
|
||||
Dual licensed under the MIT or GPL Version 2 licenses
|
||||
http://jquery.org/license
|
||||
FPDF
|
||||
FPDF is released under a permissive license: there is no usage restriction. You may embed it freely in your application (commercial or not), with or without modifications.
|
||||
http://www.fpdf.org/
|
||||
Math
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://pear.php.net/package/Math_BigInteger
|
||||
phpseclib
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
PHP_Compat /
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
http://php.net/
|
||||
Spreadsheet_Excel_Reader
|
||||
http://www.php.net/license/3_0.txt PHP License 3.0
|
||||
http://www.roxburgh.me.uk
|
||||
BarcodePHP
|
||||
http://www.barcodephp.com
|
||||
On this page, you can download free software for non-commercial use!!!
|
||||
Crypt
|
||||
http://www.gnu.org/licenses/lgpl.txt
|
||||
http://phpseclib.sourceforge.net
|
||||
pretty autocomplete
|
||||
COPYING for license information (LGPL)
|
||||
http://www.horde.org/
|
||||
jquery.mobile.min.js
|
||||
The MIT License
|
||||
http://jquerymobile.com/
|
||||
colorpicker
|
||||
COPYING for license information (LGPL).
|
||||
Original Sphere Plugin v0.1/v0.3, Design/Programming by Ulyses, (c) 2007 ColorJack.com, IE fixes by Hamish.
|
||||
grider.js
|
||||
http://www.opensource.org/licenses/mit-license.php
|
||||
Boris Barroso Camberos
|
||||
tinymce
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
moxiecode.com
|
||||
Flot
|
||||
http://code.google.com/p/flot/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
jQuery ZeroClipboard v1.1.1 (am 06.06.2016 aus Version entfernt BS)
|
||||
http://steamdev.com/zclip
|
||||
The MIT License
|
||||
Noty jQuery
|
||||
https://github.com/needim/noty
|
||||
The MIT License
|
||||
HTTPFUL
|
||||
http://phphttpclient.com
|
||||
The MIT License
|
||||
PHPExcel
|
||||
Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
|
||||
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
|
||||
Chart.js
|
||||
MIT license
|
||||
https://raw.githubusercontent.com/nnnick/Chart.js/v2.0-dev/dist/Chart.js
|
||||
ckeditor
|
||||
GNU Lesser General Public License Version 2.1 or later (the "LGPL")
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
http://ckeditor.com/
|
||||
Copyright (c) 2003-2018, CKSource Frederico Knabben. All rights reserved.
|
||||
jQuery.Gantt
|
||||
Distributed under an MIT license.
|
||||
http://taitems.github.io/jQuery.Gantt/
|
||||
|
||||
jquery-multidownload
|
||||
MIT License
|
||||
Copyright (c) 2011 Grzegorz Biesiadecki <gbiesiadecki@gmail.com>
|
||||
https://github.com/biesiad/jquery-multidownload
|
||||
|
||||
barcode
|
||||
MIT License
|
||||
Copyright (c) 2016 Kreative Software
|
||||
https://github.com/kreativekorp/barcode
|
||||
|
||||
fints-hbci-php eingebaut am 2.4.2017
|
||||
MIT License
|
||||
Copyright (c) 2016 Markus Schindler <mail@markus-schindler.de>
|
||||
https://github.com/mschindler83/fints-hbci-php
|
||||
|
||||
pdfmake
|
||||
https://github.com/bpampuch/pdfmake
|
||||
MIT Licence
|
||||
@bpampuch (founder)
|
||||
@liborm85
|
||||
pdfmake is based on a truly amazing library pdfkit (credits to @devongovett).
|
||||
|
||||
normalize
|
||||
http://necolas.github.com/normalize.css/
|
||||
The MIT License (MIT)
|
||||
Copyright © Nicolas Gallagher and Jonathan Neal
|
||||
|
||||
jquery.timeline
|
||||
MIT License © Yehia A.Salam
|
||||
https://github.com/yehiasalam/jquery.timeline
|
||||
|
||||
php-fedex-api-wrapper
|
||||
MIT License © Jeremy Dunn
|
||||
https://github.com/JeremyDunn/php-fedex-api-wrapper
|
||||
|
||||
jQuery tagEditor Plugin
|
||||
MIT License © Pixabay.com
|
||||
https://github.com/Pixabay/jQuery-tagEditor
|
||||
|
||||
ColorExtractor
|
||||
MIT License © Mathieu Lechat
|
||||
https://github.com/thephpleague/color-extractor
|
||||
|
||||
HTML Purifier
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
https://travis-ci.org/ezyang/htmlpurifier
|
||||
|
||||
rakit/validation
|
||||
MIT License
|
||||
https://github.com/rakit/validation
|
||||
|
||||
aura/sqlquery
|
||||
BSD-2-Clause
|
||||
https://github.com/auraphp/Aura.SqlQuery
|
||||
|
||||
raml2html
|
||||
MIT License
|
||||
https://github.com/raml2html/raml2html
|
||||
|
||||
raml2html werk theme
|
||||
MIT License
|
||||
https://github.com/silicann/raml2html-werk-theme
|
||||
|
||||
Nickersoft/push.js
|
||||
MIT License © Tyler Nickerson
|
||||
https://github.com/Nickersoft/push.js
|
||||
|
||||
sabre/dav
|
||||
BSD-3-Clause
|
||||
https://github.com/sabre-io/dav
|
||||
|
||||
needim/noty - A jQuery Notification Plugin
|
||||
MIT License
|
||||
https://github.com/needim/noty
|
||||
|
||||
swiss-payment-slip/swiss-payment-slip-fpdf
|
||||
MIT License
|
||||
https://github.com/ravage84/SwissPaymentSlipFpdf
|
||||
|
||||
League\Flysystem
|
||||
MIT License
|
||||
https://github.com/thephpleague/flysystem
|
||||
|
||||
hammerjs/hammer.js
|
||||
MIT License © Jorik Tangelder (Eight Media)
|
||||
https://github.com/hammerjs/hammer.js/
|
||||
|
||||
haltu/muuri
|
||||
MIT License © Haltu Oy
|
||||
https://github.com/haltu/muuri
|
||||
|
||||
FitText.js
|
||||
WTFPL License © Dave Rupert
|
||||
https://github.com/davatron5000/FitText.js
|
||||
|
||||
Smarty template engine
|
||||
LGPL-3.0 License
|
||||
https://github.com/smarty-php/smarty
|
||||
|
||||
TCPDF
|
||||
GNU-LGPL v3 © Nicola Asuni - Tecnick.com
|
||||
https://tcpdf.org
|
||||
|
||||
Guzzle, PHP HTTP client
|
||||
MIT License
|
||||
https://github.com/guzzle/guzzle
|
||||
|
||||
League/OAuth1-Client
|
||||
MIT License
|
||||
https://github.com/thephpleague/oauth1-client
|
||||
|
||||
Etsy Provider for League/OAuth1-Client
|
||||
MIT License © Y0lk <gabriel@inkrebit.com>
|
||||
https://github.com/Y0lk/oauth1-etsy
|
||||
|
||||
Lexend Deca Font
|
||||
SIL Open Font License 1.1 © The Lexend Project Authors
|
||||
https://github.com/ThomasJockin/lexend
|
||||
|
||||
Roboto Font
|
||||
Apache License, Version 2.0 © Google Inc.
|
||||
https://github.com/google/roboto
|
||||
|
||||
HOTP/TOTP Token Generator
|
||||
MIT License
|
||||
https://github.com/lfkeitel/php-totp
|
||||
|
||||
Laminas/Laminas-mail
|
||||
BSD-3-Clause
|
||||
https://github.com/laminas/laminas-mail
|
||||
|
||||
Verwenden Sie die Software nur, wenn Sie mit eine gültige Lizenz erworben haben!
|
||||
|
||||
Kontakt: WaWision GmbH
|
||||
Holzbachstraße 4
|
||||
D-86152 Augsburg
|
||||
Telefon 0821 268 41 0 41
|
||||
Telefax 0821 268 41 0 42
|
||||
E-Mail kontakt@wawision.de
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Dies ist die kommerzielle Version von Xentral.
|
||||
|
||||
Bitte beachten Sie die Bedingungen aus dem Software-Lizenzvertrag,
|
||||
welchen Sie beim Kauf unterschrieben haben.
|
||||
|
||||
Informationen zur Software finden Sie im Internet:
|
||||
|
||||
https://xentral.com/
|
||||
https://xentral.com/akademie-home
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Xentral Open-Source Version
|
||||
Bitte beachten Sie die Bedingungen der [Lizenzen](LICENSE.md).
|
||||
|
||||
Informationen zur Software finden Sie hier:
|
||||
https://xentral.com/
|
||||
https://xentral.com/akademie-home
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
mysqldump --extended-insert --no-create-db dbname -hlocalhost -uwawision -p | gzip > /var/www/backup/mysql/mysql_complete_`date +%d`.gz
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
mysqldump dbname -h127.0.0.1 -u<username> -p<password> | gzip > /tmp/mysql_complete_daily.gz
|
||||
|
||||
|
||||
ftp -inv <ipftpserver> << EOF
|
||||
user <userftp> <passwordftp>
|
||||
put /tmp/mysql_complete_daily.gz mysql_complete_`date +%y%m%d_0000`.gz
|
||||
bye
|
||||
EOF
|
||||
rm -f /tmp/mysql_complete_daily.gz
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
tar cfz /tmp/userdata.tar.gz ../../userdata
|
||||
|
||||
|
||||
ftp -inv <ipftpserver> << EOF
|
||||
user <userftp> <passwordftp>
|
||||
put /tmp/userdata.tar.gz userdata_complete_`date +%d`.tar.gz
|
||||
bye
|
||||
EOF
|
||||
rm -f /tmp/userdata.tar.gz
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
$WFdbname='';
|
||||
$WFdbuser='';
|
||||
$WFdbpass='';
|
||||
|
||||
$ftp_server="";
|
||||
$ftp_user_name = "";
|
||||
$ftp_user_pass= "";
|
||||
|
||||
$pfad = "/tmp";
|
||||
|
||||
exec("mysqldump $WFdbname -hlocalhost -u".$WFdbuser." -p".$WFdbpass." | gzip > $pfad/mysql_complete.gz");
|
||||
|
||||
$file = $pfad.'/mysql_complete.gz';
|
||||
$remote_file = "mysql_complete_".date('d').".gz";
|
||||
|
||||
// Verbindung aufbauen
|
||||
$conn_id = ftp_connect($ftp_server);
|
||||
|
||||
// Login mit Benutzername und Passwort
|
||||
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
|
||||
|
||||
//ftp_pasv($conn_id, true);
|
||||
|
||||
// Datei hochladen
|
||||
if (ftp_put($conn_id, $remote_file, $file, FTP_BINARY)) {
|
||||
} else {
|
||||
}
|
||||
|
||||
// Verbindung schlie?~_en
|
||||
ftp_close($conn_id);
|
||||
unlink($file);
|
||||
?>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
$ftp_server="";
|
||||
$ftp_user_name = "";
|
||||
$ftp_user_pass= "";
|
||||
|
||||
$pfad = "/tmp";
|
||||
|
||||
$userdata = "/var/www/wawision/userdata";
|
||||
|
||||
|
||||
exec("tar cfz $pfad/userdata.tar.gz $userdata");
|
||||
|
||||
$file = $pfad.'/userdata.tar.gz';
|
||||
|
||||
$remote_file = "userdata_".date('d').".tar.gz";
|
||||
|
||||
// Verbindung aufbauen
|
||||
$conn_id = ftp_connect($ftp_server);
|
||||
|
||||
// Login mit Benutzername und Passwort
|
||||
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
|
||||
|
||||
ftp_pasv($conn_id, true);
|
||||
|
||||
|
||||
// Datei hochladen
|
||||
if (ftp_put($conn_id, $remote_file, $file, FTP_BINARY)) {
|
||||
} else {
|
||||
}
|
||||
|
||||
// Verbindung schlie?~_en
|
||||
ftp_close($conn_id);
|
||||
unlink($file);
|
||||
?>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup\Adapter;
|
||||
|
||||
use Xentral\Components\Database\DatabaseConfig;
|
||||
|
||||
/**
|
||||
* Interface AdapterInterface
|
||||
*
|
||||
* @package Xentral\Components\Backup\Adapter
|
||||
*/
|
||||
interface AdapterInterface
|
||||
{
|
||||
/** @var string STATUS_WORKING */
|
||||
const STATUS_WORKING = 'working';
|
||||
|
||||
/** @var string STATUS_WAIT */
|
||||
const STATUS_WAITING = 'waiting';
|
||||
|
||||
/**
|
||||
* Makes MySQL DUMP
|
||||
*
|
||||
* @param DatabaseConfig $config
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @param null|string|array $sTable
|
||||
*
|
||||
* @param null|string $where
|
||||
*
|
||||
* @param bool $quickMode Without SET INNODB_STRICT_MODE=0; Advantage quick and space-saving
|
||||
*
|
||||
* @return int PidFile
|
||||
*/
|
||||
public function createDump(DatabaseConfig $config, $file, $sTable = null, $where = null, $quickMode=true);
|
||||
|
||||
/**
|
||||
* Makes Backup or System template recovery
|
||||
*
|
||||
* @param DatabaseConfig $config
|
||||
* @param string $file
|
||||
*
|
||||
* @return int pidFile
|
||||
*/
|
||||
public function restoreDump(DatabaseConfig $config, $file);
|
||||
|
||||
/**
|
||||
* returns the current status
|
||||
*
|
||||
* @param string $pidFile
|
||||
*
|
||||
* @return string|self::STATUS_WORKING|self::STATUS_WAITING
|
||||
*/
|
||||
public function getStatus($pidFile);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup\Adapter;
|
||||
|
||||
use Xentral\Components\Database\DatabaseConfig;
|
||||
|
||||
final class ExecAdapter implements AdapterInterface
|
||||
{
|
||||
|
||||
/** @var DatabaseConfig $config */
|
||||
private $config;
|
||||
|
||||
/** @var int timeout */
|
||||
const TIME_OUT = 3600;
|
||||
|
||||
/**
|
||||
* @param DatabaseConfig $config
|
||||
* @param string $file
|
||||
*
|
||||
* @param null|string|array $tables
|
||||
*
|
||||
* @param null|string $where
|
||||
*
|
||||
* @param bool $quickMode Without SET INNODB_STRICT_MODE=0; Advantage quick and space-saving
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createDump(DatabaseConfig $config, $file, $tables = null, $where = null, $quickMode = true)
|
||||
{
|
||||
$this->config = $config;
|
||||
$sAsBackup = $this->config->getDatabase();
|
||||
if ($tables !== null) {
|
||||
if (is_array($tables)) {
|
||||
$tables = implode(' ', $tables);
|
||||
}
|
||||
$sAsBackup .= ' --tables ' . $tables;
|
||||
}
|
||||
if ($where !== null) {
|
||||
$sAsBackup .= " --where=\"$where\"";
|
||||
}
|
||||
if ($quickMode !== true) {
|
||||
$cmd = "echo 'SET INNODB_STRICT_MODE=0;' > {$file} && mysqldump --no-tablespaces --extended-insert {$sAsBackup} --no-create-db -h{$this->config->getHostname()} -u{$this->config->getUsername()} -p'{$this->config->getPassword()}' >> {$file} && gzip -c {$file} > " . $file . '.gz && rm -f' . $file;
|
||||
} else {
|
||||
$cmd = "mysqldump --no-tablespaces --extended-insert {$sAsBackup} --no-create-db -h{$this->config->getHostname()} -u{$this->config->getUsername()} -p'{$this->config->getPassword()}' | gzip > " . $file . '.gz';
|
||||
}
|
||||
$this->execute($cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DatabaseConfig $config
|
||||
* @param string $file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restoreDump(DatabaseConfig $config, $file)
|
||||
{
|
||||
$this->config = $config;
|
||||
$cmd = "gunzip < {$file} | mysql -D{$this->config->getDatabase()} -h{$this->config->getHostname()} -u{$this->config->getUsername()} -p'{$this->config->getPassword()}'";
|
||||
$this->execute($cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pidFile
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus($pidFile)
|
||||
{
|
||||
if (file_exists($pidFile) && ($time = file_get_contents($pidFile)) && (time() - (int)$time) < static::TIME_OUT) {
|
||||
return AdapterInterface::STATUS_WORKING;
|
||||
}
|
||||
|
||||
return AdapterInterface::STATUS_WAITING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
*/
|
||||
protected function execute($cmd)
|
||||
{
|
||||
@exec($cmd);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup;
|
||||
|
||||
use ApplicationCore;
|
||||
use Xentral\Components\Backup\Exception\BackupException;
|
||||
use Xentral\Components\Backup\Adapter\ExecAdapter;
|
||||
use Xentral\Components\Backup\Logger\BackupLog;
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices()
|
||||
{
|
||||
return [
|
||||
'DatabaseBackup' => 'onInitDatabaseBackup',
|
||||
'FileBackup' => 'onInitFileBackup',
|
||||
'BackupLog' => 'onInitBackupLogger',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return DatabaseBackup
|
||||
*/
|
||||
public static function onInitDatabaseBackup(ContainerInterface $container)
|
||||
{
|
||||
//@codeCoverageIgnoreStart
|
||||
if (!function_exists('exec')) {
|
||||
throw new BackupException(sprintf('function "%s" is missing!', 'exec'));
|
||||
}
|
||||
//@codeCoverageIgnoreEnd
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
return new DatabaseBackup(new ExecAdapter(), $app->erp->getTMP());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return FileBackup
|
||||
*/
|
||||
public static function onInitFileBackup(ContainerInterface $container)
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
return new FileBackup($container->get('BackupLog'), $app->erp->getTMP());
|
||||
}
|
||||
|
||||
public static function onInitBackupLogger(ContainerInterface $container)
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
/** @var string $path */
|
||||
$path = $app->erp->GetRootPath() . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR;
|
||||
|
||||
return new BackupLog($path, 'status.txt');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup;
|
||||
|
||||
use Xentral\Components\Backup\Adapter\AdapterInterface;
|
||||
use Xentral\Components\Database\DatabaseConfig;
|
||||
use Xentral\Components\Backup\Exception\BackupException;
|
||||
|
||||
final class DatabaseBackup
|
||||
{
|
||||
/**
|
||||
* @var AdapterInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/** @var string $tmpPath */
|
||||
private $tmpPath;
|
||||
|
||||
/** @var string lock */
|
||||
const PID_NAME = 'backup.lock';
|
||||
|
||||
/**
|
||||
* DatabaseBackup constructor.
|
||||
*
|
||||
* @param AdapterInterface $adapter
|
||||
*
|
||||
* @param string $tmpPath
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter, $tmpPath)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->tmpPath = $tmpPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MySQL Dump
|
||||
*
|
||||
* @param DatabaseConfig $config
|
||||
* @param string $file
|
||||
* @param null|string|array $sTable
|
||||
*
|
||||
* @param null|string $where
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createDump(DatabaseConfig $config, $file, $sTable = null, $where = null)
|
||||
{
|
||||
$sPidFile = $this->getLockFile();
|
||||
file_put_contents($sPidFile, time());
|
||||
$this->adapter->createDump($config, $file, $sTable, $where);
|
||||
@unlink($sPidFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Database DUMP
|
||||
*
|
||||
* @param DatabaseConfig $config
|
||||
* @param string $file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restoreDump(DatabaseConfig $config, $file)
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
throw new BackupException(sprintf('Database Dump %s not found!', $file));
|
||||
}
|
||||
$sPidFile = $this->getLockFile();
|
||||
file_put_contents($sPidFile, time());
|
||||
$this->adapter->restoreDump($config, $file);
|
||||
@unlink($sPidFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $metaFile
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMetaInfo($metaFile)
|
||||
{
|
||||
if (!empty($metaFile) && file_exists($metaFile) && ($sMetaEnc = file_get_contents($metaFile))) {
|
||||
return $this->decodeJson(base64_decode($sMetaEnc), true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sJSON
|
||||
* @param bool $bAsHash
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function decodeJson($sJSON, $bAsHash = false)
|
||||
{
|
||||
if (($xData = json_decode($sJSON, $bAsHash)) !== null
|
||||
&& (json_last_error() === JSON_ERROR_NONE)) {
|
||||
return $xData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string|AdapterInterface
|
||||
*/
|
||||
public function getLockStatus()
|
||||
{
|
||||
return $this->adapter->getStatus($this->getLockFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getLockFile()
|
||||
{
|
||||
return rtrim($this->tmpPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . static::PID_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tables
|
||||
* @param array $excludeKeys
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function excludeCheckSumTables($tables, $excludeKeys = [])
|
||||
{
|
||||
$default = [
|
||||
'backup',
|
||||
'useronline',
|
||||
'logfile',
|
||||
'cronjob_starter_running',
|
||||
'wiki',
|
||||
'protokoll',
|
||||
'cronjob_log',
|
||||
'module_stat',
|
||||
'checkaltertable',
|
||||
'konfiguration',
|
||||
'permissionhistory',
|
||||
'adapterbox_request_log',
|
||||
'hook',
|
||||
'module_action',
|
||||
'prozessstarter',
|
||||
'sqlcache',
|
||||
'systemhealth',
|
||||
'userkonfiguration',
|
||||
'artikel',
|
||||
'shopimport_amazon_throttling',
|
||||
'lieferschein',
|
||||
'report_column',
|
||||
'report_parameter',
|
||||
'notification_message',
|
||||
'module_stat_detail',
|
||||
];
|
||||
$excludeKeys = array_merge($default, $excludeKeys);
|
||||
foreach ($excludeKeys as $key) {
|
||||
if (!array_key_exists($key, $tables)) {
|
||||
continue;
|
||||
}
|
||||
unset($tables[$key]);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $backupFile
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaFileName($backupFile)
|
||||
{
|
||||
$asFile = explode('.', $backupFile);
|
||||
array_pop($asFile);
|
||||
$filename = implode('.', $asFile) . '.meta';
|
||||
|
||||
return str_replace('.backup', 'backup/snapshots', $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDumpMetaData($filePath = null)
|
||||
{
|
||||
return $this->getMetaInfo($this->getMetaFileName($filePath));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class BackupException extends RuntimeException implements BackupExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
/**
|
||||
* Interface BackupExceptionInterface
|
||||
*
|
||||
* @package Xentral\Components\Backup\Exception
|
||||
*/
|
||||
interface BackupExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Xentral\Components\Backup\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
|
||||
class LogException extends RuntimeException implements BackupExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,451 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Backup;
|
||||
|
||||
use PHPUnit\Runner\Exception;
|
||||
use Xentral\Components\Backup\Logger\BackupLog;
|
||||
use Xentral\Components\Backup\Exception\BackupException;
|
||||
use ZipArchive;
|
||||
|
||||
final class FileBackup implements FileBackupInterface
|
||||
{
|
||||
|
||||
/** @var string backup path */
|
||||
private $sUserPath;
|
||||
|
||||
/** @var BackupLog $logger */
|
||||
private $logger;
|
||||
|
||||
/** @var string $cacheTmp */
|
||||
private $cacheTmp;
|
||||
|
||||
/**
|
||||
* @param BackupLog $logger
|
||||
* @param string $cacheTmp
|
||||
*/
|
||||
public function __construct(BackupLog $logger, $cacheTmp)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->cacheTmp = $cacheTmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getMainPath()
|
||||
{
|
||||
$asPath = explode(DIRECTORY_SEPARATOR, $this->sUserPath);
|
||||
array_pop($asPath);
|
||||
|
||||
return implode(DIRECTORY_SEPARATOR, $asPath) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the full file name path
|
||||
*
|
||||
* @param string $filename
|
||||
* @param bool $bIsSnapshots
|
||||
* @param null $userPath
|
||||
*
|
||||
* @throws BackupException
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalPath($filename, $userPath = null, $bIsSnapshots = true)
|
||||
{
|
||||
if (null !== $userPath) {
|
||||
$this->sUserPath = $userPath;
|
||||
}
|
||||
$path = $this->getMainPath();
|
||||
if ($bIsSnapshots === true) {
|
||||
$path .= FileBackupInterface::SNAPSHOTS_FOLDER . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if (!file_exists($path) && !@mkdir($path) && !is_dir($path)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not created', $path));
|
||||
throw new BackupException(sprintf('Directory "%s" was not created', $path));
|
||||
}
|
||||
|
||||
return $path . $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function tmpDir()
|
||||
{
|
||||
return $this->getMainPath() . 'backup/.backup' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSnapshotsDir()
|
||||
{
|
||||
return $this->getMainPath() . FileBackupInterface::SNAPSHOTS_FOLDER . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
protected function addLock($path)
|
||||
{
|
||||
return $this->logger->write(time(), $path, FileBackupInterface::PID_FILE, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function tryPurgePidFile()
|
||||
{
|
||||
$pidFile = $this->tmpDir() . FileBackupInterface::PID_FILE;
|
||||
|
||||
$time = file_get_contents($pidFile);
|
||||
|
||||
if ((time() - (int)$time > FileBackupInterface::TIME_OUT)) {
|
||||
return unlink($pidFile);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $userPath
|
||||
*
|
||||
* @throws BackupException
|
||||
* @return string|null
|
||||
*/
|
||||
public function begin($userPath = null)
|
||||
{
|
||||
if (null !== $userPath) {
|
||||
$this->sUserPath = $userPath;
|
||||
}
|
||||
$path = $this->tmpDir();
|
||||
|
||||
if (is_dir($path)) {
|
||||
@exec('rm -rf ' . $path);
|
||||
}
|
||||
|
||||
$backupDir = $this->getMainPath() . 'backup';
|
||||
if (file_exists($backupDir . DIRECTORY_SEPARATOR . 'status.txt')) {
|
||||
@unlink($backupDir . DIRECTORY_SEPARATOR . 'status.txt');
|
||||
}
|
||||
|
||||
if (file_exists($backupDir . DIRECTORY_SEPARATOR . 'session.txt')) {
|
||||
@unlink($backupDir . DIRECTORY_SEPARATOR . 'session.txt');
|
||||
}
|
||||
|
||||
if (!file_exists($path) && !@mkdir($path, 0777, true) && !is_dir($path)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not created', $path));
|
||||
throw new BackupException(sprintf('Directory "%s" was not created', $path));
|
||||
}
|
||||
|
||||
if ($this->getLockStatus() === FileBackupInterface::STATUS_WORKING && $this->tryPurgePidFile() === false) {
|
||||
return null;
|
||||
//throw new BackupException(sprintf('Backup is Running'));
|
||||
}
|
||||
|
||||
if ($this->addLock($path) === false) {
|
||||
$this->logger->writePersistent('Failed start backup');
|
||||
throw new BackupException('Failed start backup');
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function cleanUp($file)
|
||||
{
|
||||
$path = $this->tmpDir();
|
||||
if ($this->moveDir($path . $file, $this->getLocalPath($file)) === true) {
|
||||
return $this->deleteDir($path);
|
||||
}
|
||||
$this->logger->writePersistent(sprintf('Clean Up of %s failed', $path));
|
||||
throw new BackupException(sprintf('Clean Up of %s failed', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class_name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function classExists($class_name)
|
||||
{
|
||||
return class_exists($class_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipArchive $oZip
|
||||
* @param string $fileName
|
||||
* @param int $flags
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function openZipObject($oZip, $fileName, $flags = 0)
|
||||
{
|
||||
return $oZip->open($fileName, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $oldDir
|
||||
* @param string $newDir
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function moveDir($oldDir, $newDir)
|
||||
{
|
||||
return @rename($oldDir, $newDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDir($dir)
|
||||
{
|
||||
return @mkdir($dir) || is_dir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBackupExtension()
|
||||
{
|
||||
return FileBackupInterface::COMPRESS_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename Zipped file name
|
||||
* @param string $userPath local directory to backup
|
||||
* @param string|null $sMySQLFile MySQL Backup file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createBackup($filename, $userPath, $sMySQLFile = null)
|
||||
{
|
||||
$this->sUserPath = $userPath;
|
||||
$rootPath = realpath($userPath);
|
||||
|
||||
if (!file_exists($userPath)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not found', $userPath));
|
||||
throw new BackupException(sprintf('Directory "%s" was not found', $userPath));
|
||||
}
|
||||
|
||||
$tmpFilename = $this->tmpDir() . $filename;
|
||||
$sMySQLFullPath = $this->tmpDir() . $sMySQLFile;
|
||||
|
||||
if (null !== $sMySQLFile && is_file($sMySQLFullPath) && filesize($sMySQLFullPath) > 1024) {
|
||||
$this->logger->write('Add MySQL file to Zip');
|
||||
exec('cd ' . $rootPath . ' && mv ' . $sMySQLFullPath . ' ' . $sMySQLFile);
|
||||
}
|
||||
|
||||
exec('cd ' . $rootPath . ' && zip -r -9 ' . $tmpFilename . ' * .[^.]* -x "wiki/*"');
|
||||
|
||||
if (null !== $sMySQLFile) {
|
||||
exec('cd ' . $rootPath . ' && rm -f ' . $sMySQLFile);
|
||||
}
|
||||
|
||||
return $this->cleanUp($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dirPath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function deleteDir($dirPath)
|
||||
{
|
||||
if (is_dir($dirPath)) {
|
||||
if (substr($dirPath, strlen($dirPath) - 1, 1) !== '/') {
|
||||
$dirPath .= '/';
|
||||
}
|
||||
$files = glob($dirPath . '*', GLOB_MARK);
|
||||
foreach ($files as $file) {
|
||||
if (is_dir($file)) {
|
||||
$this->deleteDir($file);
|
||||
} else {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
return rmdir($dirPath);
|
||||
}
|
||||
$this->logger->writePersistent(sprintf('Deleted DIR %s failed', $dirPath));
|
||||
throw new BackupException(sprintf('Deleted DIR %s failed', $dirPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $backupFile
|
||||
* @param string $userPath
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function restoreFileSystem($backupFile, $userPath, $options = [])
|
||||
{
|
||||
$default = ['template_file_dir' => null, 'exclude_dir' => ['wiki']];
|
||||
$options = array_merge($default, $options);
|
||||
$templateFileDir = $options['template_file_dir'];
|
||||
$this->sUserPath = $userPath;
|
||||
$bIsSnapshots = null === $templateFileDir;
|
||||
|
||||
$this->sUserPath = null === $templateFileDir ? $userPath : $templateFileDir;
|
||||
|
||||
if (file_exists($file = $this->getLocalPath($backupFile, null, $bIsSnapshots))) {
|
||||
$userDataPath = realpath($userPath);
|
||||
$tmpExtract = $this->tmpDir() . FileBackupInterface::LOCAL_FILES_DIR_NAME . 'tmp';
|
||||
if (!file_exists($tmpExtract) && !@mkdir($tmpExtract) && !is_dir($tmpExtract)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not created', $tmpExtract));
|
||||
throw new BackupException(sprintf('Directory "%s" was not created', $tmpExtract));
|
||||
}
|
||||
|
||||
if (!$this->classExists('ZipArchive')) {
|
||||
$this->logger->writePersistent('Class ZipArchive is missing!');
|
||||
throw new BackupException('Class ZipArchive is missing!');
|
||||
}
|
||||
|
||||
$oZip = new ZipArchive();
|
||||
|
||||
if ($this->openZipObject($oZip, $file, ZipArchive::CHECKCONS) !== true) {
|
||||
$this->logger->writePersistent(sprintf('Failure to open file in "%s"', $file));
|
||||
throw new BackupException(sprintf('Failure to open file in "%s"', $file));
|
||||
}
|
||||
|
||||
$oZip->extractTo($tmpExtract);
|
||||
$oZip->close();
|
||||
|
||||
// move user data
|
||||
$shortTmp = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '.rmTmp';
|
||||
$sBeforeTmp = $shortTmp . uniqid('', true) . 'before';
|
||||
|
||||
if (!$this->isDir($sBeforeTmp)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not created', $sBeforeTmp));
|
||||
throw new BackupException(sprintf('Directory "%s" was not created', $sBeforeTmp));
|
||||
}
|
||||
|
||||
$this->logger->write('Moving userdata away');
|
||||
|
||||
if (!$this->moveDir($userDataPath, $sBeforeTmp)) {
|
||||
$this->logger->writePersistent(sprintf('Moving %s into %s failed! ', $userDataPath, $sBeforeTmp));
|
||||
throw new BackupException(sprintf('Moving %s into %s failed! ', $userDataPath, $sBeforeTmp));
|
||||
}
|
||||
|
||||
if (array_key_exists('exclude_dir', $options) && is_array($options['exclude_dir'])) {
|
||||
$this->excludeDirectory($options['exclude_dir'], $tmpExtract, $sBeforeTmp);
|
||||
}
|
||||
|
||||
$this->logger->write('Recovering userData');
|
||||
if (!$this->moveDir($tmpExtract, $userDataPath)) {
|
||||
$this->logger->writePersistent(sprintf('Moving %s into %s failed!', $tmpExtract, $userDataPath));
|
||||
throw new BackupException(sprintf('Moving %s into %s failed!', $tmpExtract, $userDataPath));
|
||||
}
|
||||
|
||||
// FIX TMP ISSUE
|
||||
if (!empty($this->cacheTmp) && is_dir($this->cacheTmp)) {
|
||||
$this->logger->write('Delete DB tmp');
|
||||
$this->deleteDir($this->cacheTmp);
|
||||
}
|
||||
|
||||
// remove DB if exists
|
||||
$backupFileExploded = explode('.', $backupFile);
|
||||
array_pop($backupFileExploded);
|
||||
$tmpSql = implode('.', $backupFileExploded) . '.sql.gz';
|
||||
if (is_file($userDataPath . DIRECTORY_SEPARATOR . $tmpSql)) {
|
||||
exec('cd ' . $userDataPath . ' && rm -f ' . $tmpSql);
|
||||
}
|
||||
|
||||
return $this->deleteDir($this->tmpDir()) && $this->deleteDir($sBeforeTmp);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $excludeDir
|
||||
* @param string $tmpDir extracted temporally directory
|
||||
* @param string $oldUserDataDir
|
||||
*/
|
||||
protected function excludeDirectory($excludeDir = [], $tmpDir, $oldUserDataDir)
|
||||
{
|
||||
foreach ($excludeDir as $directory) {
|
||||
// EXCLUDE WIKI DIRECTORY
|
||||
$keepPath = $tmpDir . DIRECTORY_SEPARATOR . $directory;
|
||||
$keepDirTmp = $oldUserDataDir . DIRECTORY_SEPARATOR . $directory;
|
||||
|
||||
if (!is_dir($keepPath) && $directory !== 'wiki') {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" cannot be skipped', $keepPath));
|
||||
throw new BackupException(sprintf('Directory "%s" cannot be skipped', $keepPath));
|
||||
}
|
||||
|
||||
if ($directory !== 'wiki') {
|
||||
$this->deleteDir($keepPath);
|
||||
}
|
||||
|
||||
if (file_exists($keepDirTmp)) {
|
||||
$oldDirTmp = rtrim(
|
||||
sys_get_temp_dir(),
|
||||
DIRECTORY_SEPARATOR
|
||||
) . DIRECTORY_SEPARATOR . '.' . $directory . 'Tmp' . uniqid('', true);
|
||||
if (!$this->isDir($oldDirTmp)) {
|
||||
$this->logger->writePersistent(sprintf('Directory "%s" was not created', $oldDirTmp));
|
||||
throw new BackupException(sprintf('Directory "%s" was not created', $oldDirTmp));
|
||||
}
|
||||
$oldDir = $oldDirTmp . DIRECTORY_SEPARATOR . $directory;
|
||||
if (!$this->moveDir($keepDirTmp, $oldDir)) {
|
||||
$this->logger->writePersistent(sprintf('Could not move %s directory into "%s"', $directory,
|
||||
$oldDir));
|
||||
throw new BackupException(sprintf('Could not move %s directory into "%s"', $directory,
|
||||
$oldDir));
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Latest WIKI Directory
|
||||
if (isset($oldDir) && is_dir($oldDir)) {
|
||||
$this->logger->write('Reset Wiki Directory');
|
||||
|
||||
if (!$this->moveDir($oldDir, $keepPath)) {
|
||||
$this->logger->writePersistent(sprintf('Could not move %s directory into "%s"', $oldDir,
|
||||
$keepPath));
|
||||
throw new BackupException(sprintf('Could not move %s directory into "%s"', $oldDir, $keepPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $userDataDir
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLockStatus($userDataDir = null)
|
||||
{
|
||||
if (null !== $userDataDir) {
|
||||
$this->sUserPath = $userDataDir;
|
||||
}
|
||||
$path = $this->tmpDir();
|
||||
if (file_exists($path . FileBackupInterface::PID_FILE) &&
|
||||
($time = file_get_contents($path . FileBackupInterface::PID_FILE)) &&
|
||||
(time() - (int)$time < FileBackupInterface::TIME_OUT)
|
||||
) {
|
||||
return FileBackupInterface::STATUS_WORKING;
|
||||
}
|
||||
|
||||
return FileBackupInterface::STATUS_WAITING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean everything without files move. This might be used, when breaking started backup job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function breakCleanUp()
|
||||
{
|
||||
return $this->deleteDir($this->tmpDir());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Xentral\Components\Backup;
|
||||
|
||||
|
||||
use Xentral\Components\Backup\Exception\BackupException;
|
||||
|
||||
interface FileBackupInterface
|
||||
{
|
||||
/** @var string STATUS_WAIT */
|
||||
const STATUS_WAITING = 'waiting';
|
||||
/** @var string STATUS_WORKING */
|
||||
const STATUS_WORKING = 'working';
|
||||
/** @var string Extension for the whole backup */
|
||||
const COMPRESS_EXTENSION = 'zip';
|
||||
/** @var string pid file */
|
||||
const PID_FILE = 'backup.lock';
|
||||
/** @var int Timeout */
|
||||
const TIME_OUT = 3600;
|
||||
/** @var string snapshots folder */
|
||||
const SNAPSHOTS_FOLDER = 'backup/snapshots';
|
||||
/** @var string user data directory */
|
||||
const LOCAL_FILES_DIR_NAME = 'userdata';
|
||||
|
||||
/**
|
||||
* @param string|null $userPath
|
||||
*
|
||||
* @throws BackupException
|
||||
* @return string|null
|
||||
*/
|
||||
public function begin($userPath = null);
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param string $userPath
|
||||
* @param string|null $sMySQLFile
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createBackup($filename, $userPath, $sMySQLFile = null);
|
||||
|
||||
/**
|
||||
* @param string $backupFile
|
||||
* @param string $userPath
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function restoreFileSystem($backupFile, $userPath, $options = []);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLockStatus();
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Xentral\Components\Backup\Logger;
|
||||
|
||||
|
||||
use Xentral\Components\Backup\Exception\LogException;
|
||||
|
||||
final class BackupLog
|
||||
{
|
||||
/** @var string|null $path */
|
||||
private $fullPath;
|
||||
|
||||
/** @var string|null $storagePath */
|
||||
private $storagePath;
|
||||
|
||||
/** @var string|null $fileName */
|
||||
private $fileName;
|
||||
|
||||
/** @var string $persistentFile */
|
||||
private static $persistentFile = 'backup_logger.txt';
|
||||
|
||||
public function __construct($path = null, $fileName = null)
|
||||
{
|
||||
if (null !== $path && null !== $fileName) {
|
||||
$this->fullPath = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $fileName;
|
||||
}
|
||||
$this->storagePath = $path;
|
||||
$this->fileName = $fileName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string|null $fileName
|
||||
*
|
||||
* @param bool $withDate
|
||||
*
|
||||
* @param bool $append
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
public function write($message, $path = null, $fileName = null, $withDate = true, $append = true)
|
||||
{
|
||||
$flag = FILE_APPEND | LOCK_EX;
|
||||
|
||||
$path = $this->getFullPath($path, $fileName);
|
||||
|
||||
if (null === $path || (!file_exists($path) && !@touch($path))) {
|
||||
throw new LogException(sprintf('cannot access or create file %s', $path));
|
||||
}
|
||||
|
||||
$message = $withDate === true ? time() . ': ' . $message : $message;
|
||||
if ($append === false) {
|
||||
$flag &= ~FILE_APPEND;
|
||||
}
|
||||
|
||||
return file_put_contents($path, $message . "\n", $flag);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $linePosition
|
||||
*
|
||||
* @param string|null $path
|
||||
*
|
||||
* @param string|null $fileName
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function tail($linePosition = 0, $path = null, $fileName = null)
|
||||
{
|
||||
$path = $this->getFullPath($path, $fileName);
|
||||
|
||||
if (null === $path || !file_exists($path)) {
|
||||
throw new LogException(sprintf('File %s cannot be found!', $path));
|
||||
}
|
||||
$output = '';
|
||||
if (($xData = file($path, FILE_SKIP_EMPTY_LINES)) && count($xData) > 0) {
|
||||
$key = (int)$linePosition === 0 ? count($xData) - 1 : $linePosition;
|
||||
if (!array_key_exists($key, $xData)) {
|
||||
throw new LogException(sprintf('Offset %d is missing', (int)$linePosition));
|
||||
}
|
||||
$output = $xData[$key];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $path
|
||||
*
|
||||
* @param string|null $fileName
|
||||
*
|
||||
* @return bool|false|string
|
||||
*/
|
||||
public function getContent($path = null, $fileName = null)
|
||||
{
|
||||
$path = $this->getFullPath($path, $fileName);
|
||||
|
||||
if (null === $path || !file_exists($path)) {
|
||||
throw new LogException(sprintf('File %s cannot be found!', $path));
|
||||
}
|
||||
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $path
|
||||
*
|
||||
* @param string|null $fileName
|
||||
*
|
||||
* @throws LogException
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path = null, $fileName = null)
|
||||
{
|
||||
$path = $this->getFullPath($path, $fileName);
|
||||
if (null !== $path && !file_exists($path)) {
|
||||
return false;
|
||||
//throw new LogException(sprintf('File %s cannot be deleted!', $path));
|
||||
}
|
||||
|
||||
return unlink($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $path
|
||||
* @param null $fileName
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function getFullPath($path = null, $fileName = null)
|
||||
{
|
||||
if ($path === null && $fileName === null) {
|
||||
return $this->fullPath;
|
||||
}
|
||||
if ($path !== null && $fileName !== null) {
|
||||
return rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $fileName;
|
||||
}
|
||||
|
||||
if ($fileName !== null && $path === null && $this->storagePath !== null) {
|
||||
return rtrim($this->storagePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $fileName;
|
||||
}
|
||||
|
||||
if ($path !== null && $fileName === null && $this->fileName !== null) {
|
||||
return rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $this->fileName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function writePersistent($message)
|
||||
{
|
||||
$this->write($message, null, self::$persistentFile, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPersistentFileName()
|
||||
{
|
||||
return self::$persistentFile;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode;
|
||||
|
||||
use Xentral\Components\Barcode\Exception\InvalidArgumentException;
|
||||
|
||||
final class BarcodeFactory
|
||||
{
|
||||
/**
|
||||
* @param string $codeText
|
||||
* @param string $ecLevel Error correction level [L|M|Q|H]
|
||||
*
|
||||
* @return Qrcode
|
||||
*/
|
||||
public function createQrCode($codeText, $ecLevel = 'L')
|
||||
{
|
||||
$codeType = 'QRCODE,' . $ecLevel;
|
||||
if (!in_array($codeType, Qrcode::$validTypes, true)) {
|
||||
throw new InvalidArgumentException('Invalid error correction level: ' . $ecLevel);
|
||||
}
|
||||
$barcode2d = new TcpdfBarcode2d($codeType, $codeText);
|
||||
|
||||
return new Qrcode($barcode2d);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices()
|
||||
{
|
||||
return [
|
||||
'BarcodeFactory' => 'onInitBarcodeFactory',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BarcodeFactory
|
||||
*/
|
||||
public static function onInitBarcodeFactory()
|
||||
{
|
||||
return new BarcodeFactory();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class BarcodeCreationFailedException extends RuntimeException implements BarcodeExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
interface BarcodeExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements BarcodeExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class MissingPhpExtensionException extends RuntimeException implements BarcodeExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode;
|
||||
|
||||
final class Qrcode
|
||||
{
|
||||
/** @var string TYPE_QRCODE */
|
||||
const TYPE_DEFAULT = 'QRCODE';
|
||||
|
||||
/** @var string TYPE_EC_LOW Low error correction */
|
||||
const TYPE_EC_LOW = 'QRCODE,L';
|
||||
|
||||
/** @var string TYPE_EC_MEDIUM Medium error correction */
|
||||
const TYPE_EC_MEDIUM = 'QRCODE,M';
|
||||
|
||||
/** @var string TYPE_EC_QUARTILE Better error correction */
|
||||
const TYPE_EC_QUARTILE = 'QRCODE,Q';
|
||||
|
||||
/** @var string TYPE_EC_HIGH Best error correction */
|
||||
const TYPE_EC_HIGH = 'QRCODE,H';
|
||||
|
||||
/** @var array $validTypes */
|
||||
public static $validTypes = [
|
||||
self::TYPE_DEFAULT,
|
||||
self::TYPE_EC_LOW,
|
||||
self::TYPE_EC_MEDIUM,
|
||||
self::TYPE_EC_QUARTILE,
|
||||
self::TYPE_EC_HIGH,
|
||||
];
|
||||
|
||||
/** @var TcpdfBarcode2d $barcode */
|
||||
private $barcode;
|
||||
|
||||
/**
|
||||
* @param TcpdfBarcode2d $barcode
|
||||
*/
|
||||
public function __construct(TcpdfBarcode2d $barcode)
|
||||
{
|
||||
$this->barcode = $barcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
return $this->barcode->getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as array representation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->barcode->getBarcodeArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as HTML representation
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in pixels.
|
||||
* @param int $height Height of a single rectangle element in pixels.
|
||||
* @param string $color Foreground color for bar elements (background is transparent).
|
||||
*
|
||||
* @return string HTML code
|
||||
*/
|
||||
public function toHtml($width = 10, $height = 10, $color = 'black')
|
||||
{
|
||||
return $this->barcode->getBarcodeHtml($width, $height, $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as SVG document
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in user units
|
||||
* @param int $height Height of a single rectangle element in user units
|
||||
* @param string $color Foreground color (in SVG format) for bar elements (background is transparent)
|
||||
*
|
||||
* @return string SVG document
|
||||
*/
|
||||
public function toSvg($width = 10, $height = 10, $color = 'black')
|
||||
{
|
||||
return $this->barcode->getBarcodeSvg($width, $height, $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as PNG image (requires GD or Imagick library)
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in pixels
|
||||
* @param int $height Height of a single rectangle element in pixels
|
||||
* @param array $color RGB-Array (0-255) foreground color for bar elements (background is transparent)
|
||||
*
|
||||
* @return string Image as string
|
||||
*/
|
||||
public function toPng($width = 10, $height = 10, $color = [0, 0, 0])
|
||||
{
|
||||
return $this->barcode->getBarcodePng($width, $height, $color);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Barcode;
|
||||
|
||||
use TCPDF2DBarcode;
|
||||
use Xentral\Components\Barcode\Exception\BarcodeCreationFailedException;
|
||||
use Xentral\Components\Barcode\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Barcode\Exception\MissingPhpExtensionException;
|
||||
|
||||
/**
|
||||
* Anti-Corruption-Layer for TCPDF2DBarcode class
|
||||
*/
|
||||
final class TcpdfBarcode2d
|
||||
{
|
||||
/** @var array $validTypes */
|
||||
public static $validTypes = [
|
||||
Qrcode::TYPE_DEFAULT,
|
||||
Qrcode::TYPE_EC_LOW,
|
||||
Qrcode::TYPE_EC_MEDIUM,
|
||||
Qrcode::TYPE_EC_QUARTILE,
|
||||
Qrcode::TYPE_EC_HIGH,
|
||||
];
|
||||
|
||||
/** @var TCPDF2DBarcode $barcode */
|
||||
private $barcode;
|
||||
|
||||
/** @var string $codeType */
|
||||
private $type;
|
||||
|
||||
/** @var string $codeText */
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $text
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BarcodeCreationFailedException
|
||||
*/
|
||||
public function __construct($type, $text)
|
||||
{
|
||||
if (empty($type)) {
|
||||
throw new InvalidArgumentException('Could not create barcode. Required parameter "type" is empty.');
|
||||
}
|
||||
if (empty($text)) {
|
||||
throw new InvalidArgumentException('Could not create barcode. Required parameter "text" is empty.');
|
||||
}
|
||||
if (!in_array($type, self::$validTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Could not create barcode. Invalid Type: "%s". Valid types: [%s]',
|
||||
$type,
|
||||
implode('|', self::$validTypes)
|
||||
));
|
||||
}
|
||||
|
||||
$barcode = new TCPDF2DBarcode($text, $type);
|
||||
if ($barcode->getBarcodeArray() === false) {
|
||||
throw new BarcodeCreationFailedException(sprintf(
|
||||
'Could not create barcode. Type "%s" - Text "%s"', $type, $text
|
||||
));
|
||||
}
|
||||
|
||||
$this->barcode = $barcode;
|
||||
$this->text = $text;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBarcodeArray()
|
||||
{
|
||||
return $this->barcode->getBarcodeArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as HTML representation
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in pixels.
|
||||
* @param int $height Height of a single rectangle element in pixels.
|
||||
* @param string $color Foreground color for bar elements (background is transparent).
|
||||
*
|
||||
* @return string HTML code
|
||||
*/
|
||||
public function getBarcodeHtml($width = 10, $height = 10, $color = 'black')
|
||||
{
|
||||
return $this->barcode->getBarcodeHTML($width, $height, $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as SVG document
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in user units
|
||||
* @param int $height Height of a single rectangle element in user units
|
||||
* @param string $color Foreground color (in SVG format) for bar elements (background is transparent)
|
||||
*
|
||||
* @return string SVG document
|
||||
*/
|
||||
public function getBarcodeSvg($width = 10, $height = 10, $color = 'black')
|
||||
{
|
||||
return $this->barcode->getBarcodeSVGcode($width, $height, $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the QR code as PNG image (requires GD or Imagick library)
|
||||
*
|
||||
* @param int $width Width of a single rectangle element in pixels
|
||||
* @param int $height Height of a single rectangle element in pixels
|
||||
* @param array $color RGB-Array (0-255) foreground color for bar elements (background is transparent)
|
||||
*
|
||||
* @throws MissingPhpExtensionException If gd and imagick extension missing
|
||||
* @throws BarcodeCreationFailedException
|
||||
*
|
||||
* @return string Image as string
|
||||
*/
|
||||
public function getBarcodePng($width, $height, $color = [0, 0, 0])
|
||||
{
|
||||
$imageData = $this->barcode->getBarcodePngData($width, $height, $color);
|
||||
if (!function_exists('imagecreate') && !extension_loaded('imagick')) {
|
||||
throw new MissingPhpExtensionException(
|
||||
'Barcode image creation failed. PHP extension "gd" or "imagick" is required; both missing.'
|
||||
);
|
||||
}
|
||||
if ($imageData === false) {
|
||||
throw new BarcodeCreationFailedException(
|
||||
'Barcode image creation failed. Unknown error.'
|
||||
);
|
||||
}
|
||||
|
||||
return $imageData;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Barcodes
|
||||
|
||||
## QR-Codes
|
||||
|
||||
### Beispiel
|
||||
|
||||
```php
|
||||
$factory = $this->app->Container->get('BarcodeFactory');
|
||||
|
||||
// Qrcode-Objekt erzeugen
|
||||
$ecLevel = 'M'; // M = Medium error correction
|
||||
$qrcode = $factory->createQrCode($qrtext, $ecLevel);
|
||||
|
||||
// Varianten für die Ausgabe
|
||||
$html = $qrcode->toHtml($width, $height, $color);
|
||||
$svg = $qrcode->toSvg($width, $height, $color);
|
||||
$png = $qrcode->toPng($width, $height, $color);
|
||||
```
|
||||
|
||||
##### Fehlerkorrektur-Levels
|
||||
|
||||
* `L` = Low / Niedrige Fehlerkorrektur (Default)
|
||||
* `M` = Medium / Mittlere Fehlerkorrektur
|
||||
* `Q` = Quartile / Bessere Fehlerkorrektur
|
||||
* `H` = High / Höchste Fehlerkorrektur
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Adapter;
|
||||
|
||||
use Generator;
|
||||
|
||||
interface AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function connect();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function beginTransaction();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function rollback();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function commit();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function lastInsertId();
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param string $statement
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function perform($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fetchAffected($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
* @param bool $includeGroupColumn
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchGroup($statement, array $values = [], $includeGroupColumn = false);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return int|float|string|false false on empty result
|
||||
*/
|
||||
public function fetchValue($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchRow($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchCol($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldCol($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAssoc($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAll($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldPairs($statement, array $values = []);
|
||||
|
||||
/**
|
||||
* Escapes values for "BOOLEAN" and (TINY)INT columns
|
||||
*
|
||||
* @param bool|null $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeBool($value, $isNullable = false);
|
||||
|
||||
/**
|
||||
* Escapes values for INT columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeInt($value, $isNullable = false);
|
||||
|
||||
/**
|
||||
* Escapes values for FLOAT, DOUBLE and REAL columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeDecimal($value, $isNullable = false);
|
||||
|
||||
/**
|
||||
* Escapes values for CHAR, VARCHAR, TEXT and BLOB columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeString($value, $isNullable = false);
|
||||
|
||||
/**
|
||||
* Escapes and quotes an identifier (column or table name)
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeIdentifier($value);
|
||||
}
|
||||
|
|
@ -0,0 +1,883 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Adapter;
|
||||
|
||||
use Generator;
|
||||
use mysqli;
|
||||
use mysqli_result;
|
||||
use mysqli_stmt;
|
||||
use Xentral\Components\Database\DatabaseConfig;
|
||||
use Xentral\Components\Database\Exception\BindParameterException;
|
||||
use Xentral\Components\Database\Exception\ConnectionException;
|
||||
use Xentral\Components\Database\Exception\EscapingException;
|
||||
use Xentral\Components\Database\Exception\QueryFailureException;
|
||||
use Xentral\Components\Database\Exception\TransactionException;
|
||||
use Xentral\Components\Database\Parser\MysqliArrayValueParser;
|
||||
use Xentral\Components\Database\Parser\MysqliNamedParameterParser;
|
||||
use Xentral\Components\Database\Profiler\ProfilerInterface;
|
||||
|
||||
final class MysqliAdapter implements AdapterInterface
|
||||
{
|
||||
/** @var mysqli|null $connection */
|
||||
private $connection;
|
||||
|
||||
/** @var DatabaseConfig $config */
|
||||
private $config;
|
||||
|
||||
/** @var bool $transactionActive */
|
||||
private $transactionActive = false;
|
||||
|
||||
/** @var int|null $reconnectCounter */
|
||||
private $reconnectCounter;
|
||||
|
||||
/** @var int $reconnectLimit */
|
||||
private $reconnectMaxCount = 5;
|
||||
|
||||
/** @var ProfilerInterface|null $profiler */
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @param DatabaseConfig $config
|
||||
* @param ProfilerInterface|null $profiler
|
||||
*/
|
||||
public function __construct(DatabaseConfig $config, ProfilerInterface $profiler = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConnectionException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ($this->connection !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->reconnectCounter === null) {
|
||||
$this->reconnectCounter = 0;
|
||||
} else {
|
||||
$this->reconnectCounter++;
|
||||
}
|
||||
|
||||
if ($this->reconnectCounter >= $this->reconnectMaxCount) {
|
||||
throw new ConnectionException(sprintf(
|
||||
'Too many reconnects. Reconnect count: %d (Max allowed %d)',
|
||||
$this->reconnectCounter,
|
||||
$this->reconnectMaxCount
|
||||
));
|
||||
}
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
|
||||
$connection = new mysqli(
|
||||
$this->config->getHostname(),
|
||||
$this->config->getUsername(),
|
||||
$this->config->getPassword(),
|
||||
null,
|
||||
$this->config->getPort()
|
||||
);
|
||||
|
||||
if ($connection->connect_errno > 0) {
|
||||
throw new ConnectionException(sprintf(
|
||||
'Database connection to host "%s" failed. Error code #%s. Error message: %s',
|
||||
$this->config->getHostname(),
|
||||
$connection->connect_errno,
|
||||
$connection->connect_error
|
||||
));
|
||||
}
|
||||
|
||||
$connection->select_db($this->config->getDatabase());
|
||||
if ($connection->errno > 0) {
|
||||
throw new ConnectionException(sprintf(
|
||||
'Database selection failed for database "%s". Error code #%s. Error message: %s',
|
||||
$this->config->getDatabase(),
|
||||
$connection->errno,
|
||||
$connection->error
|
||||
));
|
||||
}
|
||||
|
||||
// @see https://www.php.net/manual/de/mysqlinfo.concepts.charset.php
|
||||
if (!$connection->set_charset($this->config->getCharset())) {
|
||||
throw new ConnectionException(sprintf(
|
||||
'Failed to set character set "%s". Error: %s',
|
||||
$this->config->getCharset(),
|
||||
$connection->error
|
||||
));
|
||||
}
|
||||
|
||||
if (!$connection->autocommit(true)) {
|
||||
throw new ConnectionException(sprintf(
|
||||
'Failed to activate auto commit. Error: %s',
|
||||
$connection->error
|
||||
));
|
||||
}
|
||||
|
||||
$this->finishProfiler(null, [
|
||||
'dbname' => $this->config->getDatabase(),
|
||||
'host' => $this->config->getHostname(),
|
||||
'port' => $this->config->getPort(),
|
||||
]);
|
||||
|
||||
$this->connection = $connection;
|
||||
|
||||
foreach ($this->config->getQueries() as $query) {
|
||||
$this->perform($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$this->connection->close();
|
||||
$this->connection = null;
|
||||
$this->finishProfiler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return $this->transactionActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransactionException If transaction is already started
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
if ($this->inTransaction()) {
|
||||
throw new TransactionException('Transaction is already started.');
|
||||
}
|
||||
|
||||
$this->connect();
|
||||
|
||||
if ($this->connection->begin_transaction() === false) {
|
||||
throw new TransactionException(sprintf('Transaction start failed: %s', $this->connection->error));
|
||||
}
|
||||
$this->transactionActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransactionException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if (!$this->inTransaction()) {
|
||||
throw new TransactionException('Transaction not started.');
|
||||
}
|
||||
|
||||
$this->connection->commit();
|
||||
$this->connection->autocommit(true);
|
||||
$this->transactionActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransactionException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
if (!$this->inTransaction()) {
|
||||
throw new TransactionException('Transaction not started.');
|
||||
}
|
||||
|
||||
$this->connection->rollback();
|
||||
$this->connection->autocommit(true);
|
||||
$this->transactionActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function lastInsertId()
|
||||
{
|
||||
return (int)$this->connection->insert_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param string $statement
|
||||
*
|
||||
* @throws QueryFailureException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function perform($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$query = $this->getMysqliStatement($statement, $values);
|
||||
if (!is_object($query)) {
|
||||
throw new QueryFailureException(sprintf('Database query failed: %s', $this->connection->error));
|
||||
}
|
||||
|
||||
$query->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @throws QueryFailureException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fetchAffected($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$query = $this->getMysqliStatement($statement, $values);
|
||||
if (!is_object($query)) {
|
||||
throw new QueryFailureException(sprintf('Database query failed: %s', $this->connection->error));
|
||||
}
|
||||
|
||||
$affectedRows = (int)$query->affected_rows;
|
||||
$query->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $affectedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
|
||||
$data = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$data[] = $row;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAssoc($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
|
||||
$data = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assocKey = reset($row); // Fetch first array value
|
||||
$data[$assocKey] = $row;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchRow($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
if ($result->num_rows === 0) {
|
||||
$result->close();
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = $result->fetch_assoc();
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return int|float|string|false false on empty result
|
||||
*/
|
||||
public function fetchValue($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
if ($result->num_rows === 0) {
|
||||
$result->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $result->fetch_assoc();
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return reset($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchCol($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
|
||||
$data = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$firstValue = reset($row); // Fetch first array value
|
||||
$data[] = $firstValue;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @throws QueryFailureException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
if ($result->field_count !== 2) {
|
||||
throw new QueryFailureException('Field count does not match. fetchPairs() allows only two fields.');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$key = array_shift($row);
|
||||
$value = array_shift($row);
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
* @param bool $includeGroupColumn
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchGroup($statement, array $values = [], $includeGroupColumn = false)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
|
||||
$data = [];
|
||||
$includeGroupColumn = (bool)$includeGroupColumn;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$group = $includeGroupColumn === true ? reset($row) : array_shift($row); // Fetch first array value
|
||||
if (!isset($data[$group])) {
|
||||
$data[$group] = [];
|
||||
}
|
||||
$data[$group][] = $row;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAll($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
yield $row;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAssoc($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assocKey = reset($row); // Fetch first array value
|
||||
|
||||
yield $assocKey => $row;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldCol($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$firstValue = reset($row); // Fetch first array value
|
||||
|
||||
yield $firstValue;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @throws QueryFailureException
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldPairs($statement, array $values = [])
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$this->startProfiler(__FUNCTION__);
|
||||
$result = $this->getMysqliResult($statement, $values);
|
||||
$this->finishProfiler($statement, $values);
|
||||
|
||||
if ($result->field_count !== 2) {
|
||||
throw new QueryFailureException('Field count does not match. yieldPairs() allows only two fields.');
|
||||
}
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$key = array_shift($row);
|
||||
$value = array_shift($row);
|
||||
|
||||
yield $key => $value;
|
||||
}
|
||||
|
||||
$result->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for "BOOLEAN" and (TINY)INT columns
|
||||
*
|
||||
* @param bool|null $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeBool($value, $isNullable = false)
|
||||
{
|
||||
if ($isNullable === true && $value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (!is_bool($value)) {
|
||||
throw new EscapingException('Can not escape bool. Value is not a bool.');
|
||||
}
|
||||
|
||||
return $value === true ? '1' : '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for INT columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeInt($value, $isNullable = false)
|
||||
{
|
||||
if ($isNullable === true && $value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (!is_int($value)) {
|
||||
throw new EscapingException('Can not escape integer. Value is not an integer.');
|
||||
}
|
||||
|
||||
return (string)(int)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for FLOAT, DOUBLE and REAL columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeDecimal($value, $isNullable = false)
|
||||
{
|
||||
if ($isNullable === true && $value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
throw new EscapingException('Can not escape decimal. Value is not numeric.');
|
||||
}
|
||||
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for CHAR, VARCHAR, TEXT and BLOB columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeString($value, $isNullable = false)
|
||||
{
|
||||
if ($isNullable === true && $value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
if (!is_string($value)) {
|
||||
throw new EscapingException('Can not escape string. Value is not a string.');
|
||||
}
|
||||
|
||||
$this->connect();
|
||||
|
||||
return "'" . $this->connection->real_escape_string($value) . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes and quotes an identifier (column or table name)
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new EscapingException('Can not escape identifier. Passed value is not a string.');
|
||||
}
|
||||
if (empty(trim($value))) {
|
||||
throw new EscapingException('Can not escape identifier. Passed value is empty.');
|
||||
}
|
||||
|
||||
$parts = explode('.', $value);
|
||||
if (count($parts) > 2) {
|
||||
throw new EscapingException('Can not escape identifier. Identifier contains more than one dots.');
|
||||
}
|
||||
|
||||
$partsCleaned = [];
|
||||
foreach ($parts as $part) {
|
||||
if (empty(trim($part))) {
|
||||
throw new EscapingException(
|
||||
'Can not escape identifier. Parts before and after the dot can not be empty.'
|
||||
);
|
||||
}
|
||||
if (strlen($part) > 64) {
|
||||
throw new EscapingException(
|
||||
'Can not escape identifier. Identifier is too long. Only 64 characters are allowed.'
|
||||
);
|
||||
}
|
||||
$partCleaned = preg_replace('/[^A-Za-z0-9_]+/', '', $part);
|
||||
if (strlen($partCleaned) !== strlen($part)) {
|
||||
throw new EscapingException(
|
||||
'Can not escape identifier. Passed value contains invalid characters. ' .
|
||||
'Valid characters: A-Z, a-z, 0-9, Underscore'
|
||||
);
|
||||
}
|
||||
|
||||
$partsCleaned[] = '`' . $partCleaned . '`';
|
||||
}
|
||||
|
||||
return implode('.', $partsCleaned);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->connection = null;
|
||||
$this->transactionActive = false;
|
||||
$this->config = clone $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return mysqli_stmt
|
||||
*/
|
||||
private function getMysqliStatement($statement, array $values = [])
|
||||
{
|
||||
list($statement, $values) = $this->replaceArrayValues($statement, $values);
|
||||
list($rebuildStatement, $bindValues, $parameterNames) = $this->replaceNamedParameters($statement, $values);
|
||||
|
||||
$query = $this->connection->prepare($rebuildStatement);
|
||||
if ($query === false && $this->connection->errno === 2006) {
|
||||
// Code 2006 = MySQL server has gone away
|
||||
// Falls Verbindung in einen Timeout gelaufen ist
|
||||
// => Verbindung wiederherstellen und Prepare erneut probieren
|
||||
$this->disconnect();
|
||||
$this->connect();
|
||||
$query = $this->connection->prepare($rebuildStatement);
|
||||
}
|
||||
if ($query === false || !is_object($query)) {
|
||||
throw new QueryFailureException(
|
||||
sprintf(
|
||||
'Database prepare failed. Error code #%s. Error message: %s',
|
||||
$this->connection->errno,
|
||||
$this->connection->error
|
||||
),
|
||||
(int)$this->connection->errno
|
||||
);
|
||||
}
|
||||
|
||||
$this->bindParametersToMysqliStatement($query, $bindValues, $parameterNames);
|
||||
$query->execute();
|
||||
|
||||
if ($query->errno > 0) {
|
||||
throw new QueryFailureException(
|
||||
sprintf('Database query failed. Error code #%s. Error message: %s', $query->errno, $query->error),
|
||||
(int)$query->errno
|
||||
);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function replaceArrayValues($statement, array $values = [])
|
||||
{
|
||||
$parser = new MysqliArrayValueParser();
|
||||
$result = $parser->rebuild($statement, $values);
|
||||
|
||||
return [$result['statement'], $result['values']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function replaceNamedParameters($statement, array $values = [])
|
||||
{
|
||||
$parser = new MysqliNamedParameterParser();
|
||||
$result = $parser->rebuild($statement, $values);
|
||||
|
||||
return [$result['statement'], $result['values'], $result['params']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mysqli_stmt $statement
|
||||
* @param array $bindValues Values for binding
|
||||
* @param array $parameterNames Original parameter names (for debugging only)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function bindParametersToMysqliStatement($statement, $bindValues, $parameterNames)
|
||||
{
|
||||
if (empty($bindValues)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$bindTypes = '';
|
||||
foreach ($bindValues as $index => &$bindValue) {
|
||||
if (is_bool($bindValue)) {
|
||||
$bindValue = (int)$bindValue;
|
||||
$bindTypes .= 'i'; // integer
|
||||
continue;
|
||||
}
|
||||
if (is_float($bindValue)) {
|
||||
$bindTypes .= 'd'; // double
|
||||
continue;
|
||||
}
|
||||
if (is_array($bindValue)) {
|
||||
throw new BindParameterException(sprintf(
|
||||
'Can not bind parameter of type "array" to placeholder "%s".',
|
||||
$parameterNames[$index]
|
||||
));
|
||||
}
|
||||
if (is_object($bindValue)) {
|
||||
throw new BindParameterException(sprintf(
|
||||
'Can not bind parameter of type "object" to placeholder "%s".',
|
||||
$parameterNames[$index]
|
||||
));
|
||||
}
|
||||
$bindTypes .= 's'; // string
|
||||
}
|
||||
unset($bindValue);
|
||||
|
||||
$statement->bind_param($bindTypes, ...$bindValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return mysqli_result
|
||||
*/
|
||||
private function getMysqliResult($statement, array $values = [])
|
||||
{
|
||||
$query = $this->getMysqliStatement($statement, $values);
|
||||
$result = $query->get_result();
|
||||
$query->close();
|
||||
|
||||
if ($result === false) {
|
||||
throw new QueryFailureException(
|
||||
sprintf('Database query failed. Error code #%s. Error message: %s', $query->errno, $query->error),
|
||||
(int)$query->errno
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function startProfiler($methodName)
|
||||
{
|
||||
if ($this->profiler === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->profiler->start(__CLASS__, $methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function finishProfiler($statement = null, array $values = [])
|
||||
{
|
||||
if ($this->profiler === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->profiler->finish($statement, $values);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database;
|
||||
|
||||
use Xentral\Components\Database\Adapter\MysqliAdapter;
|
||||
use Xentral\Components\Database\Exception\ConfigException;
|
||||
use Xentral\Components\Database\Profiler\Profiler;
|
||||
use Xentral\Components\Database\SqlQuery\QueryFactory;
|
||||
use Xentral\Components\Logger\Context\ContextHelper;
|
||||
use Xentral\Components\Logger\MemoryLogger;
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
use Xentral\Core\DependencyInjection\ServiceContainer;
|
||||
use Xentral\Core\LegacyConfig\ConfigLoader;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices()
|
||||
{
|
||||
return [
|
||||
'Database' => 'onInitDatabase',
|
||||
'DatabaseProfiler' => 'onGetDatabaseProfiler',
|
||||
'MysqliAdapter' => 'onInitMysqliAdapter',
|
||||
'QueryFactory' => 'onInitQueryFactory',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServiceContainer $container
|
||||
*
|
||||
* @return Database
|
||||
*/
|
||||
public static function onInitDatabase(ServiceContainer $container)
|
||||
{
|
||||
return new Database($container->get('MysqliAdapter'), $container->get('QueryFactory'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryFactory
|
||||
*/
|
||||
public static function onInitQueryFactory()
|
||||
{
|
||||
return new QueryFactory('mysql');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServiceContainer $container
|
||||
*
|
||||
* @return Profiler
|
||||
*/
|
||||
public static function onGetDatabaseProfiler(ServiceContainer $container)
|
||||
{
|
||||
$request = $container->get('Request');
|
||||
return new Profiler(new MemoryLogger(new ContextHelper($request)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return MysqliAdapter
|
||||
*/
|
||||
public static function onInitMysqliAdapter(ContainerInterface $container)
|
||||
{
|
||||
$conf = ConfigLoader::load();
|
||||
|
||||
$dbHost = property_exists($conf, 'WFdbhost') ? $conf->WFdbhost : 'localhost';
|
||||
$dbPort = property_exists($conf, 'WFdbport') ? $conf->WFdbport : 3306;
|
||||
$dbName = property_exists($conf, 'WFdbname') ? $conf->WFdbname : null;
|
||||
$dbUser = property_exists($conf, 'WFdbuser') ? $conf->WFdbuser : null;
|
||||
$dbPass = property_exists($conf, 'WFdbpass') ? $conf->WFdbpass : null;
|
||||
|
||||
if (empty($dbName)) {
|
||||
throw new ConfigException('Could not connect to database. Database name is missing or empty.');
|
||||
}
|
||||
if (empty($dbUser)) {
|
||||
throw new ConfigException('Could not connect to database. Database user is missing or empty.');
|
||||
}
|
||||
if (empty($dbPass)) {
|
||||
throw new ConfigException('Could not connect to database. Database password is missing or empty.');
|
||||
}
|
||||
|
||||
$startupQueries = [
|
||||
"SET NAMES 'utf8', " .
|
||||
"CHARACTER SET 'utf8', " .
|
||||
"lc_time_names = 'de_DE', " .
|
||||
"SESSION sql_mode = '', " .
|
||||
"SESSION sql_big_selects = 1;",
|
||||
];
|
||||
$config = new DatabaseConfig($dbHost, $dbUser, $dbPass, $dbName, 'utf8', $dbPort, $startupQueries);
|
||||
|
||||
// Profiler aktivieren
|
||||
// Kann mit $container->get('DatabaseProfiler')->getContexts() abgefragt werden
|
||||
$profiler = $container->get('DatabaseProfiler');
|
||||
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE === true) {
|
||||
$profiler->setActive(true);
|
||||
}
|
||||
|
||||
return new MysqliAdapter($config, $profiler);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
<?php /** @noinspection PhpInconsistentReturnPointsInspection */
|
||||
|
||||
namespace Xentral\Components\Database;
|
||||
|
||||
use Generator;
|
||||
use Xentral\Components\Database\Adapter\AdapterInterface;
|
||||
use Xentral\Components\Database\Exception\EscapingException;
|
||||
use Xentral\Components\Database\Exception\TransactionException;
|
||||
use Xentral\Components\Database\SqlQuery\DeleteQuery;
|
||||
use Xentral\Components\Database\SqlQuery\InsertQuery;
|
||||
use Xentral\Components\Database\SqlQuery\SelectQuery;
|
||||
use Xentral\Components\Database\SqlQuery\QueryFactory;
|
||||
use Xentral\Components\Database\SqlQuery\UpdateQuery;
|
||||
|
||||
final class Database
|
||||
{
|
||||
/** @var AdapterInterface $adapter */
|
||||
private $adapter;
|
||||
|
||||
/** @var QueryFactory $queryFactory */
|
||||
private $queryFactory;
|
||||
|
||||
/**
|
||||
* @param AdapterInterface $adapter
|
||||
* @param QueryFactory $queryFactory
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter, QueryFactory $queryFactory)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->queryFactory = $queryFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aura.SqlQuery
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return SelectQuery
|
||||
*/
|
||||
public function select()
|
||||
{
|
||||
return $this->queryFactory->newSelect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InsertQuery
|
||||
*/
|
||||
public function insert()
|
||||
{
|
||||
return $this->queryFactory->newInsert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UpdateQuery
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
return $this->queryFactory->newUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DeleteQuery
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return $this->queryFactory->newDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* ENDE: Aura.SqlQuery
|
||||
*/
|
||||
|
||||
/**
|
||||
* Close database connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->adapter->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes simple queries without named parameters
|
||||
*
|
||||
* Use self::perform() for queries with named parameters.
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function exec($query)
|
||||
{
|
||||
$this->adapter->perform($query, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function lastInsertId()
|
||||
{
|
||||
return (int)$this->adapter->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransactionException If transaction is already started
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->adapter->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->adapter->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function rollBack()
|
||||
{
|
||||
$this->adapter->rollBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction()
|
||||
{
|
||||
return $this->adapter->inTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchAll($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchAll($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchAssoc($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchAssoc($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
* @param bool $includeGroupColumn
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchGroup($query, array $values = [], $includeGroupColumn = false)
|
||||
{
|
||||
return $this->adapter->fetchGroup($query, $values, (bool)$includeGroupColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchRow($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchRow($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchPairs($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchPairs($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return int|float|string|false false on empty result
|
||||
*/
|
||||
public function fetchValue($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchValue($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fetchAffected($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchAffected($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return array Empty array on empty result
|
||||
*/
|
||||
public function fetchCol($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->fetchCol($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAll($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->yieldAll($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldAssoc($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->yieldAssoc($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldPairs($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->yieldPairs($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function yieldCol($query, array $values = [])
|
||||
{
|
||||
return $this->adapter->yieldCol($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function perform($query, array $values = [])
|
||||
{
|
||||
$this->adapter->perform($query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for "BOOLEAN" and (TINY)INT columns
|
||||
*
|
||||
* @param bool|null $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeBool($value, $isNullable = false)
|
||||
{
|
||||
return $this->adapter->escapeBool($value, $isNullable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for INT columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeInt($value, $isNullable = false)
|
||||
{
|
||||
return $this->adapter->escapeInt($value, $isNullable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for FLOAT, DOUBLE and REAL columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeDecimal($value, $isNullable = false)
|
||||
{
|
||||
return $this->adapter->escapeDecimal($value, $isNullable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes values for CHAR, VARCHAR, TEXT and BLOB columns
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $isNullable
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeString($value, $isNullable = false)
|
||||
{
|
||||
return $this->adapter->escapeString($value, $isNullable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes and quotes an identifier (column or table name)
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws EscapingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
return $this->adapter->escapeIdentifier($value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database;
|
||||
|
||||
final class DatabaseConfig
|
||||
{
|
||||
/** @var string $hostname */
|
||||
private $hostname;
|
||||
|
||||
/** @var string $username */
|
||||
private $username;
|
||||
|
||||
/** @var string $password */
|
||||
private $password;
|
||||
|
||||
/** @var string $database */
|
||||
private $database;
|
||||
|
||||
/** @var string $charset */
|
||||
private $charset;
|
||||
|
||||
/** @var int $port */
|
||||
private $port;
|
||||
|
||||
/** @var array $queries */
|
||||
private $queries;
|
||||
|
||||
/**
|
||||
* @param string $hostname
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $database
|
||||
* @param string|null $charset
|
||||
* @param int|null $port
|
||||
* @param array $queries
|
||||
*/
|
||||
public function __construct(
|
||||
$hostname,
|
||||
$username,
|
||||
$password,
|
||||
$database,
|
||||
$charset = null,
|
||||
$port = null,
|
||||
array $queries = []
|
||||
) {
|
||||
$this->hostname = (string)$hostname;
|
||||
$this->username = (string)$username;
|
||||
$this->password = (string)$password;
|
||||
$this->database = (string)$database;
|
||||
$this->charset = $charset !== null ? (string)$charset : 'utf8';
|
||||
$this->port = $port !== null ? (int)$port : 3306;
|
||||
$this->queries = $queries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
*
|
||||
* @return DatabaseConfig
|
||||
*/
|
||||
public static function fromArray(array $config)
|
||||
{
|
||||
return new DatabaseConfig(
|
||||
$config['hostname'],
|
||||
$config['username'],
|
||||
$config['password'],
|
||||
$config['database'],
|
||||
isset($config['charset']) ? $config['charset'] : null,
|
||||
isset($config['port']) ? $config['port'] : null,
|
||||
isset($config['queries']) ? $config['queries'] : []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHostname()
|
||||
{
|
||||
return $this->hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase()
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCharset()
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQueries()
|
||||
{
|
||||
return $this->queries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
return [
|
||||
'args' => [
|
||||
'hostname' => $this->hostname,
|
||||
'username' => '****',
|
||||
'password' => '****',
|
||||
'database' => $this->database,
|
||||
'charset' => $this->charset,
|
||||
'port' => $this->port,
|
||||
'queries' => $this->queries,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class BindParameterException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class ConfigException extends \InvalidArgumentException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class ConnectionException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @deprecated Will be removed in 19.4
|
||||
*/
|
||||
class DatabaseException extends RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
interface DatabaseExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class EscapingException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class MissingParameterException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class NotConnectedException extends \LogicException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class QueryFailureException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Exception;
|
||||
|
||||
class TransactionException extends \RuntimeException implements DatabaseExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Parser;
|
||||
|
||||
/**
|
||||
* @example
|
||||
* self::rebuild('SELECT * FROM foo WHERE id IN (:ids)', ['ids' => [1, 2, 3]])
|
||||
* Erzeugt:
|
||||
* [
|
||||
* 'statement' => 'SELECT * FROM foo WHERE id IN (:ids_expl_0_, :ids_expl_1_, :ids_expl_2_)',
|
||||
* 'values' => [
|
||||
* '_ids_expl_0_' => 1,
|
||||
* '_ids_expl_1_' => 2,
|
||||
* '_ids_expl_2_' => 3,
|
||||
* ]
|
||||
* ]
|
||||
*/
|
||||
final class MysqliArrayValueParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
* - Array key 'statement' contains the rebuild statement
|
||||
* - Array key 'values' contains the rebuild bind parameters
|
||||
*/
|
||||
public function rebuild($statement, array $values = [])
|
||||
{
|
||||
return $this->replaceArrayValues($statement, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function replaceArrayValues($statement, array $values = [])
|
||||
{
|
||||
foreach ($values as $paramName => $paramValue) {
|
||||
if (is_array($paramValue)) {
|
||||
$counter = 0;
|
||||
$additionalParams = [];
|
||||
foreach ($paramValue as $arrayValue) {
|
||||
$additionalParamName = '_' . $paramName . '_expl_' . $counter . '_';
|
||||
$additionalParams[] = ':' . $additionalParamName;
|
||||
$values[$additionalParamName] = $arrayValue;
|
||||
$counter++;
|
||||
}
|
||||
|
||||
// Replace original named parameter by exploded parameters in statement
|
||||
$replaceString = implode(', ', $additionalParams);
|
||||
$statement = str_replace(':' . $paramName, $replaceString, $statement);
|
||||
|
||||
// Remove original parameter value
|
||||
unset($values[$paramName]);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'statement' => $statement,
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Parser;
|
||||
|
||||
use Xentral\Components\Database\Exception\MissingParameterException;
|
||||
|
||||
/**
|
||||
* Responsibility of this class is to make sql statement and bind values compatible with mysqli
|
||||
*
|
||||
* (Mysqli does not support named parameters)
|
||||
*
|
||||
* It does this by:
|
||||
* - Replacing named parameters (:param) by ?-Placeholder (in statement)
|
||||
* - Rearranging bind values in order of appearance of named parameters
|
||||
*/
|
||||
final class MysqliNamedParameterParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rebuild($statement, array $values = [])
|
||||
{
|
||||
return $this->replaceNamedParameters($statement, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @throws MissingParameterException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function replaceNamedParameters($statement, array $values = [])
|
||||
{
|
||||
$result = [
|
||||
'statement' => $statement,
|
||||
'values' => [],
|
||||
'params' => [],
|
||||
];
|
||||
|
||||
if (empty($values)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Split statement on named parameters
|
||||
$parts = preg_split('/(:[a-zA-Z0-9_]+)/um', $statement, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($parts as &$part) {
|
||||
if (strpos($part, ':') !== 0) {
|
||||
continue; // SQL part does not contain named parameter
|
||||
}
|
||||
|
||||
$parameterName = substr_replace($part, '', 0, 1);
|
||||
if (!array_key_exists($parameterName, $values)) {
|
||||
throw new MissingParameterException(sprintf(
|
||||
'Parameter "%s" is missing from the bound values',
|
||||
$parameterName
|
||||
));
|
||||
}
|
||||
|
||||
// Push values in same order of parameters for binding
|
||||
$result['values'][] = $values[$parameterName];
|
||||
$result['params'][] = $parameterName; // For debugging only
|
||||
|
||||
// Replace named parameter by ?-Placeholder
|
||||
$part = '?';
|
||||
}
|
||||
unset($part);
|
||||
|
||||
// Rebuild statement from (changed) parts
|
||||
$result['statement'] = implode('', $parts);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Parser;
|
||||
|
||||
interface ParserInterface
|
||||
{
|
||||
/**
|
||||
* @param string $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
* - Array key 'statement' contains the corrected statement
|
||||
* - Array key 'values' contains the corrected bind values
|
||||
*/
|
||||
public function rebuild($statement, array $values = []);
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Profiler;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Logger\LoggerInterface;
|
||||
|
||||
final class Profiler implements ProfilerInterface
|
||||
{
|
||||
/** @var LoggerInterface $logger */
|
||||
private $logger;
|
||||
|
||||
/** @var bool $active */
|
||||
private $active = false;
|
||||
|
||||
/** @var string $logLevel */
|
||||
private $logLevel = 'debug';
|
||||
|
||||
/** @var string $logFormat */
|
||||
private $logFormat = "{method} ({duration}): {statement} \n{backtrace}";
|
||||
|
||||
/** @var array $context */
|
||||
private $context = [];
|
||||
|
||||
/** @var array $contexts */
|
||||
private $contexts = [];
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $methodName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start($className, $methodName)
|
||||
{
|
||||
if (!$this->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->context = [
|
||||
'class' => $className,
|
||||
'method' => $methodName,
|
||||
'start' => microtime(true),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finish($statement = null, array $values = [])
|
||||
{
|
||||
if (!$this->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
$finish = microtime(true);
|
||||
$exception = new Exception();
|
||||
|
||||
$this->context['finish'] = $finish;
|
||||
$this->context['duration_real'] = $finish - $this->context['start'];
|
||||
$this->context['duration'] = sprintf('%.6f', $this->context['duration_real']) . ' seconds';
|
||||
$this->context['statement'] = $statement;
|
||||
$this->context['bindings'] = $values;
|
||||
$this->context['backtrace'] = $exception->getTraceAsString();
|
||||
|
||||
$this->logger->log($this->logLevel, $this->logFormat, $this->context);
|
||||
|
||||
$this->contexts[] = $this->context;
|
||||
$this->context = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setActive($active)
|
||||
{
|
||||
$this->active = (bool)$active;
|
||||
}
|
||||
|
||||
/**
|
||||
* /**
|
||||
* @return string
|
||||
*/
|
||||
public function getLogLevel()
|
||||
{
|
||||
return $this->logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $logLevel
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLogLevel($logLevel)
|
||||
{
|
||||
$this->logLevel = (string)$logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoggerInterface
|
||||
*/
|
||||
public function getLogger()
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getContexts()
|
||||
{
|
||||
return $this->contexts;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\Profiler;
|
||||
|
||||
use Xentral\Components\Logger\LoggerInterface;
|
||||
|
||||
interface ProfilerInterface
|
||||
{
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $methodName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start($className, $methodName);
|
||||
|
||||
/**
|
||||
* @param string|null $statement
|
||||
* @param array $values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finish($statement = null, array $values = []);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive();
|
||||
|
||||
/**
|
||||
* @param bool $active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setActive($active);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLogLevel();
|
||||
|
||||
/**
|
||||
* @param string $logLevel
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLogLevel($logLevel);
|
||||
|
||||
/**
|
||||
* @return LoggerInterface
|
||||
*/
|
||||
public function getLogger();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\SqlQuery;
|
||||
|
||||
use Aura\SqlQuery\Mysql\Delete;
|
||||
|
||||
final class DeleteQuery extends Delete
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\SqlQuery;
|
||||
|
||||
use Aura\SqlQuery\Mysql\Insert;
|
||||
|
||||
final class InsertQuery extends Insert
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\SqlQuery;
|
||||
|
||||
use Aura\SqlQuery\AbstractQuery;
|
||||
use Aura\SqlQuery\QueryFactory as AuraQueryFactory;
|
||||
|
||||
final class QueryFactory extends AuraQueryFactory
|
||||
{
|
||||
/**
|
||||
* @return SelectQuery
|
||||
*/
|
||||
public function newSelect()
|
||||
{
|
||||
return $this->newInstance('Select');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InsertQuery
|
||||
*/
|
||||
public function newInsert()
|
||||
{
|
||||
$insert = $this->newInstance('Insert');
|
||||
$insert->setLastInsertIdNames($this->last_insert_id_names);
|
||||
|
||||
return $insert;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UpdateQuery
|
||||
*/
|
||||
public function newUpdate()
|
||||
{
|
||||
return $this->newInstance('Update');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DeleteQuery
|
||||
*/
|
||||
public function newDelete()
|
||||
{
|
||||
return $this->newInstance('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query The query object type.
|
||||
*
|
||||
* @return AbstractQuery
|
||||
*/
|
||||
protected function newInstance($query)
|
||||
{
|
||||
$class = "Xentral\\Components\\Database\\SqlQuery\\{$query}Query";
|
||||
|
||||
return new $class(
|
||||
$this->getQuoter(),
|
||||
$this->newSeqBindPrefix()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\SqlQuery;
|
||||
|
||||
use Aura\SqlQuery\Mysql\Select;
|
||||
use Closure;
|
||||
|
||||
final class SelectQuery extends Select
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOrderBy()
|
||||
{
|
||||
return !empty($this->order_by);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $andor
|
||||
* @param array|callable $args
|
||||
*
|
||||
* @return Select
|
||||
*/
|
||||
protected function addWhere($andor, $args)
|
||||
{
|
||||
if ($args[0] instanceof Closure) {
|
||||
$this->addClauseCondClosure('where', $andor, $args[0]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
return parent::addWhere($andor, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aura.SqlQuery 2.x unterstützt keine Klammersetzung in WHERE-Bedingungen
|
||||
*
|
||||
* @see https://github.com/auraphp/Aura.SqlQuery/issues/97
|
||||
*
|
||||
* Feature aus Version 3 importiert: https://github.com/auraphp/Aura.SqlQuery/pull/136/files
|
||||
*
|
||||
* @copyright Paul M. Jones
|
||||
* @license MIT
|
||||
*
|
||||
* @param string $clause
|
||||
* @param string $andor
|
||||
* @param callable $closure
|
||||
*/
|
||||
protected function addClauseCondClosure($clause, $andor, $closure)
|
||||
{
|
||||
// retain the prior set of conditions, and temporarily reset the clause
|
||||
// for the closure to work with (otherwise there will be an extraneous
|
||||
// opening AND/OR keyword)
|
||||
$set = $this->$clause;
|
||||
$this->$clause = [];
|
||||
|
||||
// invoke the closure, which will re-populate the $this->$clause
|
||||
$closure($this);
|
||||
|
||||
// are there new clause elements?
|
||||
if (!$this->$clause) {
|
||||
// no: restore the old ones, and done
|
||||
$this->$clause = $set;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// append an opening parenthesis to the prior set of conditions,
|
||||
// with AND/OR as needed ...
|
||||
if ($set) {
|
||||
$set[] = "{$andor} (";
|
||||
} else {
|
||||
$set[] = "(";
|
||||
}
|
||||
|
||||
// append the new conditions to the set, with indenting
|
||||
foreach ($this->$clause as $cond) {
|
||||
$set[] = " {$cond}";
|
||||
}
|
||||
$set[] = ")";
|
||||
|
||||
// ... then put the full set of conditions back into $this->$clause
|
||||
$this->$clause = $set;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Database\SqlQuery;
|
||||
|
||||
use Aura\SqlQuery\Mysql\Update;
|
||||
|
||||
final class UpdateQuery extends Update
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# Data Manipulation
|
||||
|
||||
## `perform()`
|
||||
|
||||
Für alle SQL Anweisung außer `SELECT`. Die Methode gibt nichts zurück. Sollte die Ausführung
|
||||
fehlschlagen, so wird eine Exception geworfen.
|
||||
|
||||
```php
|
||||
$sql = 'UPDATE foo SET bar = :bar WHERE id = :id';
|
||||
|
||||
$values = [
|
||||
'bar' => 'baz',
|
||||
'id' => 123,
|
||||
];
|
||||
|
||||
$db->perform($sql, $values);
|
||||
```
|
||||
|
||||
## `fetchAffected()`
|
||||
|
||||
Für `INSERT`, `REPLACE`, `UPDATE` und `DELETE` Anweisungen. Gibt die Anzahl der betroffenen Datensätze zurück.
|
||||
|
||||
```php
|
||||
$sql = 'UPDATE foo SET bar = :bar WHERE 1';
|
||||
|
||||
$values = [
|
||||
'bar' => 'baz',
|
||||
];
|
||||
|
||||
echo $db->fetchAffected($sql, $values);
|
||||
```
|
||||
|
||||
###### Ausgabe
|
||||
|
||||
```
|
||||
42
|
||||
```
|
||||
|
||||
## `lastInsertId()`
|
||||
|
||||
Gibt den zuletzt erzeugten Auto-Increment-Wert zurück.
|
||||
|
||||
```php
|
||||
$sql = 'INSERT INTO foo (id, bar) VALUES (NULL, :bar)';
|
||||
|
||||
$values = [
|
||||
'bar' => 'baz',
|
||||
];
|
||||
|
||||
$db->perform($sql, $values);
|
||||
// $db->fetchAffected($sql, $values); // Alternative
|
||||
|
||||
echo $db->lastInsertId();
|
||||
```
|
||||
|
||||
###### Ausgabe
|
||||
```
|
||||
123
|
||||
```
|
||||
|
||||
## Multiple-Row-Insert
|
||||
|
||||
```php
|
||||
$sql = 'INSERT INTO foo (id, bar) VALUES (NULL, :bar1, :baz1), (NULL, :bar2, :baz2), (NULL, :bar3, :baz3)';
|
||||
|
||||
$values = [
|
||||
'bar1' => 'bar',
|
||||
'baz1' => 'baz',
|
||||
'bar2' => 'barbar',
|
||||
'baz2' => 'bazbaz',
|
||||
'bar3' => 'barbarbar',
|
||||
'baz3' => 'bazbazbaz',
|
||||
];
|
||||
|
||||
$db->perform($sql, $values);
|
||||
// $db->fetchAffected($sql, $values); // Alternative; Rückgabe wäre `3`
|
||||
```
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
|
||||
# Datensätze abrufen
|
||||
|
||||
## `fetchAll()`
|
||||
|
||||
```php
|
||||
$data = $db->fetchAll('SELECT a.id, a.typ, a.name_de FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 6,
|
||||
'typ' => 'produkt',
|
||||
'name_de' => 'LED Anzeige RLED 24-8',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 7,
|
||||
'typ' => 'produkt',
|
||||
'name_de' => 'Schalter S3 24V 5A',
|
||||
),
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## `fetchAssoc()`
|
||||
|
||||
Wie `fetchAll()` allerdings wird das Ergebnis der ersten Spalte als Index verwendet.
|
||||
|
||||
```php
|
||||
$data = $db->fetchAssoc('SELECT a.id, a.typ, a.name_de FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
array (
|
||||
6 =>
|
||||
array (
|
||||
'id' => 6,
|
||||
'typ' => 'produkt',
|
||||
'name_de' => 'LED Anzeige RLED 24-8',
|
||||
),
|
||||
7 =>
|
||||
array (
|
||||
'id' => 7,
|
||||
'typ' => 'produkt',
|
||||
'name_de' => 'Schalter S3 24V 5A',
|
||||
),
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## `fetchRow()`
|
||||
|
||||
Liefert die Ergebnisse der ersten Zeile als assoziatives Array. Die Spaltennamen werden als Index verwendet.
|
||||
|
||||
```php
|
||||
$data = $db->fetchRow('SELECT a.id, a.name_de, a.name_en, a.logdatei FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
array (
|
||||
'id' => 6,
|
||||
'name_de' => 'LED Anzeige RLED 24-8',
|
||||
'name_en' => '',
|
||||
'logdatei' => '2015-10-26 17:26:27',
|
||||
)
|
||||
```
|
||||
|
||||
## `fetchValue()`
|
||||
|
||||
Liefert nur das Ergebnis der ersten Zeile und Spalte zurück.
|
||||
|
||||
```php
|
||||
$data = $db->fetchValue('SELECT a.name_de, name_en, a.logdatei FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
'LED Anzeige RLED 24-8'
|
||||
```
|
||||
|
||||
## `fetchCol()`
|
||||
|
||||
Liefert nur die Ergebnisse der ersten Spalte zurück.
|
||||
|
||||
```php
|
||||
$data = $db->fetchCol('SELECT a.name_de, a.name_en, a.logdatei FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
array (
|
||||
0 => 'LED Anzeige RLED 24-8',
|
||||
1 => 'Schalter S3 24V 5A',
|
||||
2 => 'Gehäuse GHK5 20x30x10',
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## `fetchPairs()`
|
||||
|
||||
Gibt ein (eindimentionales) assoziatives Array zurück. Der Wert der ersten Spalte wird als Index verwendet und die zweite Spalte als Wert.
|
||||
**Erwartet werden genau zwei Spalten, andernfalls wird eine Exception geworfen.**
|
||||
|
||||
```php
|
||||
$data = $db->fetchPairs('SELECT a.nummer, a.name_de FROM artikel AS a WHERE a.id > 5');
|
||||
```
|
||||
```
|
||||
array (
|
||||
700006 => 'LED Anzeige RLED 24-8',
|
||||
700007 => 'Schalter S3 24V 5A',
|
||||
700008 => 'Gehäuse GHK5 20x30x10',
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## `fetchGroup()`
|
||||
|
||||
Verhält sich wie `fetchAssoc()`; mit der Ausnahme dass die Werte der ersten Spalte gruppiert werden.
|
||||
|
||||
```php
|
||||
$data = $db->fetchGroup(
|
||||
'SELECT a.typ, a.nummer, a.name_de, a.logdatei FROM artikel AS a WHERE a.id > 5'
|
||||
);
|
||||
```
|
||||
```
|
||||
array (
|
||||
'produkt' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'typ' => 'produkt',
|
||||
'nummer' => '700006',
|
||||
'name_de' => 'LED Anzeige RLED 24-8',
|
||||
'logdatei' => '2015-10-26 17:26:27',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'typ' => 'produkt',
|
||||
'nummer' => '700007',
|
||||
'name_de' => 'Schalter S3 24V 5A',
|
||||
'logdatei' => '2015-10-26 17:26:59',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'typ' => 'produkt',
|
||||
'nummer' => '700008',
|
||||
'name_de' => 'Gehäuse GHK5 20x30x10',
|
||||
'logdatei' => '2018-05-23 05:18:52',
|
||||
),
|
||||
),
|
||||
'gebuehr' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'typ' => 'gebuehr',
|
||||
'nummer' => '100001',
|
||||
'name_de' => 'Versandkosten',
|
||||
'logdatei' => '2018-06-17 10:09:36',
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Database-Komponente
|
||||
|
||||
###### Verwendete Libraries
|
||||
|
||||
* **Aura.SqlQuery**:
|
||||
* Composer: `aura/sqlquery:2.7.*`
|
||||
* Packagist: https://packagist.org/packages/aura/sqlquery
|
||||
* GitHub: https://github.com/auraphp/Aura.SqlQuery
|
||||
* Docs: https://github.com/auraphp/Aura.SqlQuery/tree/2.x
|
||||
|
||||
* **~~Aura.Sql~~** Wird in 19.4 entfernt
|
||||
* Composer: `aura/sql:3.*`
|
||||
* Packagist: https://packagist.org/packages/aura/sql
|
||||
* GitHub: https://github.com/auraphp/Aura.Sql
|
||||
* Docs: https://github.com/auraphp/Aura.Sql/blob/3.x/docs/index.md
|
||||
|
||||
##### Database-Komponente aus Container holen
|
||||
|
||||
```php
|
||||
$db = $container->get('Database');
|
||||
```
|
||||
|
||||
Im alten Bereich:
|
||||
|
||||
```php
|
||||
$db = $this->app->Container->get('Database');
|
||||
```
|
||||
|
||||
## Themen
|
||||
|
||||
* [Datensätze abrufen (fetch)](fetch_results.md)
|
||||
* [Datensätze abrufen mit Generatoren (yield)](yield_results.md)
|
||||
* [Datensätze ändern](data_manipulation.md)
|
||||
* [Transaktionen](transactions.md)
|
||||
* [Named Parameter / Prepared Statements](named_parameter.md)
|
||||
* [SQL Query Builder](query_builder.md)
|
||||
|
||||
## Exceptions
|
||||
|
||||
Die Database-Komponente verwendet intern `mysqli`. Im Unterschied zu `mysqli` werden in Fehlerfällen aber
|
||||
Exceptions geworfen; z.B.:
|
||||
|
||||
* Wenn die Verbindung zur Datenbank fehlschlägt => `ConnectionException`
|
||||
* Wenn ein SQL-Statement fehlerhaft ist oder aus anderen Gründen nicht erfolgreich ausgeführt
|
||||
werden kann => `QueryFailureException`
|
||||
* Wenn `Named Parameter` fehlen => `MissingParameterException`
|
||||
|
||||
##### ExceptionInterface
|
||||
|
||||
Alle Exceptions die von der Database-Komponente geworfen werden implementieren das
|
||||
`\Xentral\Components\Database\Exception\DatabaseExceptionInterface` Interface.
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Named Parameter
|
||||
|
||||
`perform()`, `fetch*()` und `yield*()`-Methoden nehmen als zweiten Parameter ein assoziatives Array entgegen.
|
||||
Mit diesem Array werden lassen sich Werte als *Named Parameter* 'binden'.
|
||||
|
||||
```php
|
||||
$sql =
|
||||
'SELECT a.id, a.nummer, a.name_de
|
||||
FROM artikel AS a
|
||||
WHERE a.typ IN (:types)
|
||||
AND a.nummer LIKE :nummers
|
||||
LIMIT :start, :length';
|
||||
|
||||
$values = [
|
||||
'types' => ['produkt', 'gebuehr'],
|
||||
'nummers' => '7000%',
|
||||
'start' => 0,
|
||||
'length' => 3,
|
||||
];
|
||||
|
||||
$data = $db->fetchAll($sql, $values);
|
||||
```
|
||||
```
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => 1,
|
||||
'nummer' => '700001',
|
||||
'name_de' => 'Schraube M10x20',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'id' => 2,
|
||||
'nummer' => '700002',
|
||||
'name_de' => 'Sechskant-Mutter M10',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'id' => 3,
|
||||
'nummer' => '700003',
|
||||
'name_de' => 'Schalthebel 20x10',
|
||||
),
|
||||
)
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Profiler
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/** @var \Xentral\Components\Database\Profiler\Profiler $profiler */
|
||||
$profiler = $container->get('DatabaseProfiler');
|
||||
$profiler->setActive(true);
|
||||
|
||||
$database->fetchAll($sql);
|
||||
|
||||
var_dump($profiler->getContexts());
|
||||
```
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
# SQL Query Builder
|
||||
|
||||
###### Verwendete Libraries
|
||||
|
||||
* **Aura.SqlQuery**:
|
||||
* Composer: `aura/sqlquery:2.7.*`
|
||||
* Packagist: https://packagist.org/packages/aura/sqlquery
|
||||
* GitHub: https://github.com/auraphp/Aura.SqlQuery
|
||||
* Docs: https://github.com/auraphp/Aura.SqlQuery/tree/2.x
|
||||
|
||||
## Query Builder erzeugen
|
||||
|
||||
```php
|
||||
$select = $db->select();
|
||||
$update = $db->update();
|
||||
$insert = $db->insert();
|
||||
$delete = $db->delete();
|
||||
```
|
||||
|
||||
## SELECT-Query
|
||||
|
||||
https://github.com/auraphp/Aura.SqlQuery/tree/2.x#select
|
||||
|
||||
###### Beispiel mit Named-Placeholder
|
||||
|
||||
```php
|
||||
$select = $db->select();
|
||||
$select
|
||||
->cols(['u.id', 'u.description'])
|
||||
->from('user AS u')
|
||||
->where('u.id = :user_id')
|
||||
->bindValue('user_id', 1);
|
||||
|
||||
$result = $db->fetchAll(
|
||||
$select->getStatement(),
|
||||
$select->getBindValues()
|
||||
);
|
||||
|
||||
var_export($result);
|
||||
// array(
|
||||
// 0 => array(
|
||||
// 'id' => 1,
|
||||
// 'description' => 'Administrator',
|
||||
// ),
|
||||
// )
|
||||
```
|
||||
|
||||
###### Alternative mit ?-Placeholder
|
||||
|
||||
```php
|
||||
$select = $db->select();
|
||||
$select
|
||||
->cols(['u.id', 'u.description'])
|
||||
->from('user AS u')
|
||||
->where('u.id = ?', 1);
|
||||
```
|
||||
|
||||
###### Beispiel mit Verschachtelung im WHERE
|
||||
|
||||
```php
|
||||
$select = $db->select();
|
||||
$select
|
||||
->cols(['u.id', 'u.description'])
|
||||
->from('user AS u')
|
||||
->where('u.activ = ?', 1)
|
||||
->where(function (SelectQuery $query) {
|
||||
$query->where('u.type = ?', 'admin')
|
||||
->orWhere('u.type = ?', 'standard');
|
||||
});
|
||||
|
||||
echo $select->getStatement();
|
||||
```
|
||||
```sql
|
||||
SELECT
|
||||
`u`.`id`,
|
||||
`u`.`description`
|
||||
FROM
|
||||
`user` AS `u`
|
||||
WHERE
|
||||
`u`.`activ` = :_1_1_
|
||||
AND (
|
||||
`u`.`type` = :_1_2_
|
||||
OR `u`.`type` = :_1_3_
|
||||
)
|
||||
```
|
||||
|
||||
## INSERT-Query
|
||||
|
||||
https://github.com/auraphp/Aura.SqlQuery/tree/2.x#insert
|
||||
|
||||
## UPDATE-Query
|
||||
|
||||
https://github.com/auraphp/Aura.SqlQuery/tree/2.x#update
|
||||
|
||||
## DELETE-Query
|
||||
|
||||
https://github.com/auraphp/Aura.SqlQuery/tree/2.x#delete
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Transaktionnen
|
||||
|
||||
## Transaktion starten
|
||||
|
||||
```php
|
||||
$db->beginTransaction();
|
||||
```
|
||||
|
||||
## Transaktion übernehmen / Commit
|
||||
|
||||
```php
|
||||
$db->commit();
|
||||
```
|
||||
|
||||
## Transaktion zurücknehmen / Rollback
|
||||
|
||||
```php
|
||||
$db->rollback();
|
||||
```
|
||||
|
||||
## Prüfen ob Transaktion gestartet ist
|
||||
|
||||
```php
|
||||
$db->inTransaction();
|
||||
```
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
# Datensätze abrufen mit Generatoren
|
||||
|
||||
Um den Arbeitsspeicherverbrauch gering zu halten bieten sich zum Iterieren von großen Datenmengen
|
||||
Generatoren an: https://www.php.net/manual/de/language.generators.overview.php
|
||||
|
||||
Die Database-Komponente stellt für diesen Zweck `yield`-Methoden zur Verfügung.
|
||||
|
||||
|
||||
## `yieldAll()`
|
||||
|
||||
Wie `fetchAll()`; jede Zeile ist ein assoziatives Array.
|
||||
|
||||
```php
|
||||
$statement = 'SELECT a.id, a.typ, a.name_de FROM artikel AS a WHERE a.typ = :typ LIMIT 2';
|
||||
$bindValues = ['typ' => 'produkt'];
|
||||
|
||||
foreach ($db->yieldAll($statement, $bindValues) as $row) {
|
||||
var_dump($row);
|
||||
}
|
||||
```
|
||||
```
|
||||
array (size=3)
|
||||
'id' => int 2
|
||||
'typ' => string 'produkt' (length=7)
|
||||
'name_de' => string 'Sechskant-Mutter M10' (length=20)
|
||||
|
||||
array (size=3)
|
||||
'id' => int 3
|
||||
'typ' => string 'produkt' (length=7)
|
||||
'name_de' => string 'Schalthebel 20x10' (length=17)
|
||||
```
|
||||
|
||||
|
||||
## `yieldAssoc()`
|
||||
|
||||
Wie `fetchAssoc()`; jede Zeile ist ein assoziatives Array; der Key beinhaltet den Wert der ersten Spalte
|
||||
|
||||
```php
|
||||
$statement = 'SELECT a.id, a.typ, a.name_de FROM artikel AS a WHERE a.typ = :typ LIMIT 2';
|
||||
$bindValues = ['typ' => 'produkt'];
|
||||
|
||||
foreach ($db->yieldAssoc($statement, $bindValues) as $key => $row) {
|
||||
var_dump($key);
|
||||
var_dump($row);
|
||||
}
|
||||
```
|
||||
```
|
||||
int 2
|
||||
array (size=3)
|
||||
'id' => int 2
|
||||
'typ' => string 'produkt' (length=7)
|
||||
'name_de' => string 'Sechskant-Mutter M10' (length=20)
|
||||
|
||||
int 3
|
||||
array (size=3)
|
||||
'id' => int 3
|
||||
'typ' => string 'produkt' (length=7)
|
||||
'name_de' => string 'Schalthebel 20x10' (length=17)
|
||||
```
|
||||
|
||||
|
||||
## `yieldPairs()`
|
||||
|
||||
Wie `fetchPairs()`; jede Zeile besteht aus Key-Value-Paaren; der Key beinhaltet den Inhalt der ersten Spalte;
|
||||
der Wert den Inhalt der zweiten Spalte.
|
||||
|
||||
**Erwartet werden genau zwei Spalten, andernfalls wird eine Exception geworfen.**
|
||||
|
||||
```php
|
||||
$statement = 'SELECT a.id, a.name_de FROM artikel AS a WHERE a.typ = :typ LIMIT 2';
|
||||
$bindValues = ['typ' => 'produkt'];
|
||||
|
||||
foreach ($db->yieldPairs($statement, $bindValues) as $key => $value) {
|
||||
var_dump($key);
|
||||
var_dump($value);
|
||||
}
|
||||
```
|
||||
```
|
||||
int 2
|
||||
string 'Sechskant-Mutter M10' (length=20)
|
||||
|
||||
int 3
|
||||
string 'Schalthebel 20x10' (length=17)
|
||||
```
|
||||
|
||||
|
||||
## `yieldCol()`
|
||||
|
||||
Wie `fetchCol()`; jede Zeile beinhaltet nur den Wert der ersten Spalte.
|
||||
|
||||
```php
|
||||
$statement = 'SELECT a.name_de FROM artikel AS a WHERE a.typ = :typ LIMIT 2';
|
||||
$bindValues = ['typ' => 'produkt'];
|
||||
|
||||
foreach ($db->yieldCol($statement, $bindValues) as $key => $value) {
|
||||
var_dump($key);
|
||||
var_dump($value);
|
||||
}
|
||||
```
|
||||
```
|
||||
int 0
|
||||
string 'Sechskant-Mutter M10' (length=20)
|
||||
|
||||
int 1
|
||||
string 'Schalthebel 20x10' (length=17)
|
||||
```
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Components\EnvironmentConfig;
|
||||
|
||||
use SplFileInfo;
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
use Config;
|
||||
use License;
|
||||
use Xentral\Modules\Api\LegacyBridge\LegacyApplication;
|
||||
|
||||
class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices(): array
|
||||
{
|
||||
return [
|
||||
'EnvironmentConfig' => 'onInitEnvironmentConfig',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return EnvironmentConfig
|
||||
*/
|
||||
public static function onInitEnvironmentConfig(ContainerInterface $container): EnvironmentConfig
|
||||
{
|
||||
$provider = self::onInitEnvironmentConfigProvider($container);
|
||||
|
||||
return $provider->createEnvironmentConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return EnvironmentConfigProvider
|
||||
*/
|
||||
private static function onInitEnvironmentConfigProvider(ContainerInterface $container): EnvironmentConfigProvider
|
||||
{
|
||||
/** @var LegacyApplication $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
if (!class_exists('License')) {
|
||||
$test = dirname(__DIR__, 3) . '/phpwf/plugins/class.license.php';
|
||||
$file = new SplFileInfo($test);
|
||||
if ($file->isFile()) {
|
||||
include $test;
|
||||
}
|
||||
}
|
||||
|
||||
return new EnvironmentConfigProvider(new License(), $app->Conf);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\EnvironmentConfig;
|
||||
|
||||
final class EnvironmentConfig
|
||||
{
|
||||
/**
|
||||
* @var string $databaseHost
|
||||
*/
|
||||
private $databaseHost;
|
||||
|
||||
/**
|
||||
* @var string $databaseName
|
||||
*/
|
||||
private $databaseName;
|
||||
|
||||
/**
|
||||
* @var string $databaseUser
|
||||
*/
|
||||
private $databaseUser;
|
||||
|
||||
/**
|
||||
* @var string $databasePassword
|
||||
*/
|
||||
private $databasePassword;
|
||||
|
||||
/**
|
||||
* @var int $databasePort
|
||||
*/
|
||||
private $databasePort;
|
||||
|
||||
/**
|
||||
* @var string $userdataDirectoryPath
|
||||
*/
|
||||
private $userdataDirectoryPath;
|
||||
|
||||
/**
|
||||
* @var array|null $ioncubeSystemInformation
|
||||
*/
|
||||
private $ioncubeSystemInformation;
|
||||
|
||||
/**
|
||||
* @param string $databaseHost
|
||||
* @param string $databaseName
|
||||
* @param string $databaseUser
|
||||
* @param string $databasePassword
|
||||
* @param int $databasePort
|
||||
* @param string $userdataDirectoryPath
|
||||
* @param array $ioncubeSystemInformation
|
||||
*/
|
||||
public function __construct(
|
||||
string $databaseHost,
|
||||
string $databaseName,
|
||||
string $databaseUser,
|
||||
string $databasePassword,
|
||||
int $databasePort,
|
||||
string $userdataDirectoryPath,
|
||||
?array $ioncubeSystemInformation
|
||||
) {
|
||||
$this->databaseHost = $databaseHost;
|
||||
$this->databaseName = $databaseName;
|
||||
$this->databaseUser = $databaseUser;
|
||||
$this->databasePassword = $databasePassword;
|
||||
$this->databasePort = $databasePort;
|
||||
$this->userdataDirectoryPath = $userdataDirectoryPath;
|
||||
$this->ioncubeSystemInformation = $ioncubeSystemInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseHost(): string
|
||||
{
|
||||
return $this->databaseHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseName(): string
|
||||
{
|
||||
return $this->databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseUser(): string
|
||||
{
|
||||
return $this->databaseUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabasePassword(): string
|
||||
{
|
||||
return $this->databasePassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDatabasePort(): int
|
||||
{
|
||||
return $this->databasePort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserdataDirectoryPath(): string
|
||||
{
|
||||
return $this->userdataDirectoryPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?array
|
||||
*/
|
||||
public function getIoncubeSystemInformation(): ?array
|
||||
{
|
||||
return $this->ioncubeSystemInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSystemHostedOnCloud(): bool
|
||||
{
|
||||
return !empty($this->ioncubeSystemInformation['iscloud']['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSystemFlaggedAsDevelopmentVersion(): bool
|
||||
{
|
||||
return !empty($this->ioncubeSystemInformation['isdevelopmentversion']['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSystemFlaggedAsTestVersion(): bool
|
||||
{
|
||||
return !empty($this->ioncubeSystemInformation['testlizenz']['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxUser(): int
|
||||
{
|
||||
if (!isset($this->ioncubeSystemInformation['maxuser']['value'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)$this->ioncubeSystemInformation['maxuser']['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxLightUser(): int
|
||||
{
|
||||
if (!isset($this->ioncubeSystemInformation['maxlightuser']['value'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)$this->ioncubeSystemInformation['maxlightuser']['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getExpirationTimeStamp(): ?int
|
||||
{
|
||||
if (!isset($this->ioncubeSystemInformation['expdate']['value'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)$this->ioncubeSystemInformation['expdate']['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValueOfSpecificIoncubeSystemInformation(string $name): ?string
|
||||
{
|
||||
if ($this->ioncubeSystemInformation === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->ioncubeSystemInformation)) {
|
||||
return $this->ioncubeSystemInformation[$name]['value'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSystemFallbackEmailAddresses(): array
|
||||
{
|
||||
$emailAddresses = [];
|
||||
$mailAddressSelfBuyCustomer = (string)$this->getValueOfSpecificIoncubeSystemInformation('buyemail');
|
||||
if ($mailAddressSelfBuyCustomer !== '') {
|
||||
$emailAddresses[] = $mailAddressSelfBuyCustomer;
|
||||
}
|
||||
|
||||
$mailAddressCustomerLicence = (string)$this->getValueOfSpecificIoncubeSystemInformation('emaillicence');
|
||||
if ($mailAddressCustomerLicence !== ''
|
||||
&& strpos($mailAddressCustomerLicence, '@') !== false
|
||||
&& strpos($mailAddressCustomerLicence, '@xentral.com') === false
|
||||
&& strpos($mailAddressCustomerLicence, '@xentral.biz') === false) {
|
||||
$emailAddresses[] = $mailAddressCustomerLicence;
|
||||
}
|
||||
|
||||
//in old licences email-address of customer can be insert in name instead email
|
||||
$nameCustomerLicence = (string)$this->getValueOfSpecificIoncubeSystemInformation('namelicence');
|
||||
if ($nameCustomerLicence !== ''
|
||||
&& strpos($nameCustomerLicence, '@') !== false
|
||||
&& strpos($nameCustomerLicence, '@xentral.com') === false
|
||||
&& strpos($nameCustomerLicence, '@xentral.biz') === false) {
|
||||
$emailAddresses[] = $nameCustomerLicence;
|
||||
}
|
||||
|
||||
return $emailAddresses;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\EnvironmentConfig;
|
||||
|
||||
use Config;
|
||||
use License;
|
||||
|
||||
final class EnvironmentConfigProvider
|
||||
{
|
||||
/** @var License $license */
|
||||
private $license;
|
||||
|
||||
/** @var Config $config */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param License $license
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(License $license, Config $config)
|
||||
{
|
||||
$this->license = $license;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EnvironmentConfig
|
||||
*/
|
||||
public function createEnvironmentConfig(): EnvironmentConfig
|
||||
{
|
||||
$environmentConfig = new EnvironmentConfig(
|
||||
$this->config->WFdbhost, $this->config->WFdbname, $this->config->WFdbuser,
|
||||
$this->config->WFdbpass, $this->config->WFdbport, $this->config->WFuserdata,
|
||||
(array)$this->license->getProperties()
|
||||
);
|
||||
|
||||
return $environmentConfig;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Collection;
|
||||
|
||||
use ArrayObject;
|
||||
use AppendIterator;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
use Xentral\Components\Exporter\Exception\InvalidArgumentException;
|
||||
|
||||
final class DataCollection implements Iterator
|
||||
{
|
||||
/** @var AppendIterator $data */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* @param array|Iterator $data
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(...$data)
|
||||
{
|
||||
$this->data = new AppendIterator();
|
||||
foreach ($data as $item) {
|
||||
$this->append($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|Iterator|IteratorAggregate $data
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function append($data)
|
||||
{
|
||||
$type = gettype($data);
|
||||
if ($type === 'object') {
|
||||
$type = get_class($data);
|
||||
if ($data instanceof Iterator) {
|
||||
$type = 'Iterator';
|
||||
}
|
||||
if ($data instanceof IteratorAggregate) {
|
||||
$type = 'IteratorAggregate';
|
||||
}
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'array':
|
||||
$this->data->append((new ArrayObject($data))->getIterator());
|
||||
break;
|
||||
|
||||
case 'Iterator':
|
||||
$this->data->append($data);
|
||||
break;
|
||||
|
||||
case 'IteratorAggregate':
|
||||
$this->data->append($data->getIterator());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Unsupported type "%s".', $type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
*
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->data->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->data->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
*
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->data->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
*
|
||||
* @return boolean Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->data->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->data->rewind();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Collection;
|
||||
|
||||
use ArrayObject;
|
||||
use Closure;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
use Xentral\Components\Exporter\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Exporter\Exception\InvalidReturnTypeException;
|
||||
|
||||
final class FormatterCollection implements Iterator
|
||||
{
|
||||
/** @var Iterator $data */
|
||||
private $data;
|
||||
|
||||
/** @var callable $callback */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* @param array|Iterator $data
|
||||
* @param callable|Closure $callback
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($data, $callback)
|
||||
{
|
||||
if (!is_callable($callback, false)) {
|
||||
throw new InvalidArgumentException('Callback is not callable');
|
||||
}
|
||||
$this->callback = $callback;
|
||||
|
||||
$type = gettype($data);
|
||||
if ($type === 'object') {
|
||||
$type = get_class($data);
|
||||
if ($data instanceof Iterator) {
|
||||
$type = 'Iterator';
|
||||
}
|
||||
if ($data instanceof IteratorAggregate) {
|
||||
$type = 'IteratorAggregate';
|
||||
}
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'array':
|
||||
$this->data = (new ArrayObject($data))->getIterator();
|
||||
break;
|
||||
|
||||
case 'Iterator':
|
||||
$this->data = $data;
|
||||
break;
|
||||
|
||||
case 'IteratorAggregate':
|
||||
$this->data = $data->getIterator();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Unsupported type "%s".', $type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
*
|
||||
* @throws InvalidReturnTypeException
|
||||
*
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$result = call_user_func($this->callback, $this->data->current());
|
||||
|
||||
if (!is_array($result)) {
|
||||
throw new InvalidReturnTypeException('Formatter return type is invalid . Callable must return an array.');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->data->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
*
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->data->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
*
|
||||
* @return boolean Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->data->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->data->rewind();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Csv;
|
||||
|
||||
final class CsvConfig
|
||||
{
|
||||
/** @var string $delimiter */
|
||||
private $delimiter;
|
||||
|
||||
/** @var string $enclosure */
|
||||
private $enclosure;
|
||||
|
||||
/** @var string $escapeChar */
|
||||
private $escapeChar;
|
||||
|
||||
/** @var string $sourceCharset */
|
||||
private $sourceCharset;
|
||||
|
||||
/** @var string $targetCharset */
|
||||
private $targetCharset;
|
||||
|
||||
/** @var bool $forceEnclosureEnabled */
|
||||
private $forceEnclosureEnabled;
|
||||
|
||||
/**
|
||||
* @param string $delimiter
|
||||
* @param string $enclosure
|
||||
* @param string $escapeChar
|
||||
* @param string $targetCharset
|
||||
* @param string $sourceCharset
|
||||
* @param bool $forceEnclosureEnabled
|
||||
*/
|
||||
public function __construct(
|
||||
$delimiter = ',',
|
||||
$enclosure = '"',
|
||||
$escapeChar = "\\",
|
||||
$targetCharset = 'UTF-8',
|
||||
$sourceCharset = 'UTF-8',
|
||||
$forceEnclosureEnabled = false
|
||||
) {
|
||||
$this->delimiter = $delimiter;
|
||||
$this->enclosure = $enclosure;
|
||||
$this->escapeChar = $escapeChar;
|
||||
$this->targetCharset = $targetCharset;
|
||||
$this->sourceCharset = $sourceCharset;
|
||||
$this->forceEnclosureEnabled = $forceEnclosureEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEnclosure()
|
||||
{
|
||||
return $this->enclosure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEscapeChar()
|
||||
{
|
||||
return $this->escapeChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSourceCharset()
|
||||
{
|
||||
return $this->sourceCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetCharset()
|
||||
{
|
||||
return $this->targetCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $delimiter
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $enclosure
|
||||
*/
|
||||
public function setEnclosure($enclosure)
|
||||
{
|
||||
$this->enclosure = $enclosure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $escapeChar
|
||||
*/
|
||||
public function setEscapeChar($escapeChar)
|
||||
{
|
||||
$this->escapeChar = $escapeChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sourceCharset
|
||||
*/
|
||||
public function setSourceCharset($sourceCharset)
|
||||
{
|
||||
$this->sourceCharset = $sourceCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $targetCharset
|
||||
*/
|
||||
public function setTargetCharset($targetCharset)
|
||||
{
|
||||
$this->targetCharset = $targetCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isForceEnclosureEnabled()
|
||||
{
|
||||
return $this->forceEnclosureEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $forceEnclosureEnabled
|
||||
*/
|
||||
public function setForceEnclosureEnabled($forceEnclosureEnabled)
|
||||
{
|
||||
$this->forceEnclosureEnabled = $forceEnclosureEnabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Csv;
|
||||
|
||||
use Iterator;
|
||||
use Xentral\Components\Exporter\Exception\FileExistsException;
|
||||
use Xentral\Components\Exporter\Exception\InvalidResourceException;
|
||||
|
||||
final class CsvExporter
|
||||
{
|
||||
/** @var CsvConfig $config */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param CsvConfig|null $config
|
||||
*/
|
||||
public function __construct(CsvConfig $config = null)
|
||||
{
|
||||
if ($config === null) {
|
||||
$config = new CsvConfig();
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Resource used for writing
|
||||
* @param array|Iterator $data Multi-dimentional array, Generator or Iterator
|
||||
*
|
||||
* @throws FileExistsException|InvalidResourceException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function export($filePath, $data)
|
||||
{
|
||||
$resource = $this->exportToResource($filePath, $data);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ::export() beside that the created resource will be returned
|
||||
*
|
||||
* @param string $filePath Resource used for writing
|
||||
* @param array|Iterator $data Multi-dimentional array, Generator or Iterator
|
||||
*
|
||||
* @throws FileExistsException|InvalidResourceException
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function exportToResource($filePath, $data)
|
||||
{
|
||||
if (is_file($filePath)) {
|
||||
throw new FileExistsException(sprintf('File creation failed. File "%s" already exists.', $filePath));
|
||||
}
|
||||
|
||||
// 'x+' = Create and open for reading and writing.
|
||||
// File pointer will be placed at the beginning of the file.
|
||||
// If the file already exists `fopen` will return false.
|
||||
// 'b' = Enable binary mode
|
||||
$resource = @fopen($filePath, 'x+b');
|
||||
if ($resource === false) {
|
||||
throw new InvalidResourceException(sprintf('Failed to open resource for file path "%s".', $filePath));
|
||||
}
|
||||
|
||||
$writer = new CsvWriter($resource, $this->config);
|
||||
$writer->writeLines($data);
|
||||
|
||||
return $resource;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Csv;
|
||||
|
||||
use Iterator;
|
||||
use Xentral\Components\Exporter\Exception\InvalidResourceException;
|
||||
use Xentral\Components\Exporter\Exception\PhpExtensionMissingException;
|
||||
|
||||
final class CsvWriter
|
||||
{
|
||||
/** @var resource $handle */
|
||||
private $handle;
|
||||
|
||||
/** @var CsvConfig $config */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @param CsvConfig|null $config
|
||||
*
|
||||
* @throws InvalidResourceException If resource is not writable or invalid
|
||||
* @throws PhpExtensionMissingException If mbstring is missing
|
||||
*/
|
||||
public function __construct($handle, CsvConfig $config = null)
|
||||
{
|
||||
if (!is_resource($handle)) {
|
||||
throw new InvalidResourceException('First parameter is not a valid resource.');
|
||||
}
|
||||
if (!$this->isStreamWritable($handle)) {
|
||||
throw new InvalidResourceException('Resource is not writable.');
|
||||
}
|
||||
if (!function_exists('mb_convert_encoding')) {
|
||||
throw new PhpExtensionMissingException('Required PHP extension "mbstring" is missing.');
|
||||
}
|
||||
if ($config === null) {
|
||||
$config = new CsvConfig();
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
$this->handle = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|Iterator $lines
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function writeLines($lines)
|
||||
{
|
||||
foreach ($lines as $line) {
|
||||
$this->writeLine($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $line
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function writeLine($line)
|
||||
{
|
||||
if ($this->config->isForceEnclosureEnabled()) {
|
||||
$fields = $this->encloseAllValues($line);
|
||||
fwrite(
|
||||
$this->handle,
|
||||
sprintf(
|
||||
"%s\n",
|
||||
implode($this->config->getDelimiter(), $this->convertCharset($fields))
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fputcsv(
|
||||
$this->handle,
|
||||
$this->convertCharset($line),
|
||||
$this->config->getDelimiter(),
|
||||
$this->config->getEnclosure(),
|
||||
$this->config->getEscapeChar()
|
||||
);
|
||||
}
|
||||
|
||||
/***
|
||||
* @param array $line
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function encloseAllValues($line)
|
||||
{
|
||||
$escapeChar = $this->config->getEscapeChar();
|
||||
$enclosure = $this->config->getEnclosure();
|
||||
$result = [];
|
||||
foreach ($line as $key => $value) {
|
||||
$value = str_replace($escapeChar . $enclosure, $enclosure, $value);
|
||||
$value = str_replace($enclosure, $escapeChar . $enclosure, $value);
|
||||
$result[$key] = sprintf('%1$s%2$s%1$s', $enclosure, $value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $line
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function convertCharset($line)
|
||||
{
|
||||
if ($this->config->getSourceCharset() === $this->config->getTargetCharset()) {
|
||||
return $line; // No conversion needed
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($line as $key => $cellData) {
|
||||
$result[$key] = mb_convert_encoding(
|
||||
$cellData,
|
||||
$this->config->getTargetCharset(),
|
||||
$this->config->getSourceCharset()
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isStreamWritable($handle)
|
||||
{
|
||||
$meta = stream_get_meta_data($handle);
|
||||
$currentMode = $meta['mode'];
|
||||
|
||||
$writeModes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'];
|
||||
foreach ($writeModes as $writeMode) {
|
||||
if (strpos($currentMode, $writeMode) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
interface ExporterExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FileExistsException extends RuntimeException implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidJsonException extends Exception implements ExporterExceptionInterface
|
||||
{
|
||||
/**
|
||||
* @param int $errorCode
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromJsonError($errorCode)
|
||||
{
|
||||
$exception = new self(self::mapJsonError($errorCode));
|
||||
return $exception;
|
||||
}
|
||||
|
||||
private static function mapJsonError($jsonError)
|
||||
{
|
||||
switch ($jsonError) {
|
||||
case JSON_ERROR_NONE:
|
||||
$msg = 'Unknown error';
|
||||
break;
|
||||
case JSON_ERROR_DEPTH:
|
||||
$msg = 'The maximum stack depth has been exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$msg = 'Invalid or malformed JSON';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$msg = 'Control character error, possibly incorrectly encoded';
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX:
|
||||
$msg = 'Syntax error';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
case JSON_ERROR_RECURSION:
|
||||
$msg = 'One or more recursive references in the value to be encoded';
|
||||
break;
|
||||
case JSON_ERROR_INF_OR_NAN:
|
||||
$msg = 'One or more NAN or INF values in the value to be encoded';
|
||||
break;
|
||||
case JSON_ERROR_UNSUPPORTED_TYPE:
|
||||
$msg = 'A value of a type that cannot be encoded was given';
|
||||
break;
|
||||
case JSON_ERROR_INVALID_PROPERTY_NAME:
|
||||
$msg = 'A property name that cannot be encoded was given';
|
||||
break;
|
||||
case JSON_ERROR_UTF16:
|
||||
$msg = 'Malformed UTF-16 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$msg = 'Unknown Error';
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class InvalidResourceException extends RuntimeException implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class InvalidReturnTypeException extends LogicException implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PhpExtensionMissingException extends RuntimeException implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ResourceWriteException extends Exception implements ExporterExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Json;
|
||||
|
||||
final class JsonConfig
|
||||
{
|
||||
/** @var int $options */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @see https://www.php.net/manual/en/json.constants.php
|
||||
*
|
||||
* @param int $options
|
||||
*/
|
||||
public function __construct($options = 0)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Json;
|
||||
|
||||
use Xentral\Components\Exporter\Exception\FileExistsException;
|
||||
use Xentral\Components\Exporter\Exception\InvalidResourceException;
|
||||
|
||||
final class JsonExporter
|
||||
{
|
||||
/** @var JsonConfig $config */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param JsonConfig|null $config
|
||||
*/
|
||||
public function __construct(JsonConfig $config = null)
|
||||
{
|
||||
if ($config === null) {
|
||||
$config = new JsonConfig();
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Resource used for writing
|
||||
* @param array $data Multi-dimentional array
|
||||
*
|
||||
* @throws \Xentral\Components\Exporter\Exception\InvalidJsonException
|
||||
* @throws \Xentral\Components\Exporter\Exception\ResourceWriteException
|
||||
* @return void
|
||||
*/
|
||||
public function export($filePath, $data)
|
||||
{
|
||||
$resource = $this->exportToResource($filePath, $data);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ::export() beside that the created resource will be returned
|
||||
*
|
||||
* @param string $filePath Resource used for writing
|
||||
* @param array $data Multi-dimentional array
|
||||
*
|
||||
* @throws \Xentral\Components\Exporter\Exception\InvalidJsonException
|
||||
* @throws \Xentral\Components\Exporter\Exception\ResourceWriteException
|
||||
* @return resource
|
||||
*/
|
||||
public function exportToResource($filePath, $data)
|
||||
{
|
||||
if (is_file($filePath)) {
|
||||
throw new FileExistsException(sprintf('File creation failed. File "%s" already exists.', $filePath));
|
||||
}
|
||||
|
||||
// 'x+' = Create and open for reading and writing.
|
||||
// File pointer will be placed at the beginning of the file.
|
||||
// If the file already exists `fopen` will return false.
|
||||
// 'b' = Enable binary mode
|
||||
$resource = @fopen($filePath, 'x+b');
|
||||
if ($resource === false) {
|
||||
throw new InvalidResourceException(sprintf('Failed to open resource for file path "%s".', $filePath));
|
||||
}
|
||||
|
||||
$writer = new JsonWriter($resource, $this->config);
|
||||
$writer->write($data);
|
||||
|
||||
return $resource;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Exporter\Json;
|
||||
|
||||
use JsonSerializable;
|
||||
use Xentral\Components\Exporter\Exception\InvalidJsonException;
|
||||
use Xentral\Components\Exporter\Exception\InvalidResourceException;
|
||||
use Xentral\Components\Exporter\Exception\PhpExtensionMissingException;
|
||||
use Xentral\Components\Exporter\Exception\ResourceWriteException;
|
||||
|
||||
final class JsonWriter
|
||||
{
|
||||
/** @var resource $handle */
|
||||
private $handle;
|
||||
|
||||
/** @var JsonConfig $config */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @param JsonConfig|null $config
|
||||
*
|
||||
* @throws InvalidResourceException If resource is not writable or invalid
|
||||
* @throws PhpExtensionMissingException If mbstring is missing
|
||||
*/
|
||||
public function __construct($handle, JsonConfig $config = null)
|
||||
{
|
||||
if (!is_resource($handle)) {
|
||||
throw new InvalidResourceException('First parameter is not a valid resource.');
|
||||
}
|
||||
if (!$this->isStreamWritable($handle)) {
|
||||
throw new InvalidResourceException('Resource is not writable.');
|
||||
}
|
||||
if (!function_exists('mb_convert_encoding')) {
|
||||
throw new PhpExtensionMissingException('Required PHP extension "mbstring" is missing.');
|
||||
}
|
||||
if ($config === null) {
|
||||
$config = new JsonConfig();
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
$this->handle = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|JsonSerializable $data
|
||||
*
|
||||
* @throws PhpExtensionMissingException If json is missing
|
||||
* @throws ResourceWriteException
|
||||
* @throws InvalidJsonException
|
||||
*/
|
||||
public function write($data)
|
||||
{
|
||||
if (!function_exists('json_encode')) {
|
||||
throw new PhpExtensionMissingException('Required PHP extension "json" is missing.');
|
||||
}
|
||||
|
||||
$jsonOptions = $this->config->getOptions();
|
||||
$jsonString = @json_encode($data, $jsonOptions);
|
||||
|
||||
if ($jsonString === false) {
|
||||
throw InvalidJsonException::fromJsonError(json_last_error());
|
||||
}
|
||||
|
||||
$writeResult = @fwrite($this->handle, $jsonString);
|
||||
if ($writeResult === false) {
|
||||
throw new ResourceWriteException("JSON could not be written to resource.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isStreamWritable($handle)
|
||||
{
|
||||
$meta = stream_get_meta_data($handle);
|
||||
$currentMode = $meta['mode'];
|
||||
|
||||
$writeModes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'];
|
||||
foreach ($writeModes as $writeMode) {
|
||||
if (strpos($currentMode, $writeMode) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# CSV-Exporter
|
||||
|
||||
## Datei erstellen
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Exporter\Csv\CsvExporter;
|
||||
|
||||
/** @var Database $db */
|
||||
$db = $container->get('Database');
|
||||
$data = $db->yieldAll('SELECT e.* FROM employees AS e');
|
||||
|
||||
$filePath = tempnam(sys_get_temp_dir(), 'employees') . '.csv';
|
||||
|
||||
$exporter = new CsvExporter();
|
||||
$exporter->export($filePath, $data);
|
||||
```
|
||||
|
||||
##### Rückgabewerte
|
||||
|
||||
* Die `export()` Methode liefert nichts zurück.
|
||||
* Die `exportToResource()` gibt die geöffnete Ressource zurück. Die Ressource muss manuell (mit `fclose()`)
|
||||
geschlossen werden. Der Dateizeiger ist auf `EOF` platziert.
|
||||
|
||||
##### Funktionsparameter
|
||||
|
||||
Beide `export*()`-Methoden nehmen die gleichen Parameter entgegen.
|
||||
|
||||
* Als erster Parameter wird ein absoluter Dateipfad erwartet. Alternativ kann ein Stream Wrapper angegeben werden.
|
||||
Beispiele:
|
||||
* `php://output` um die CSV direkt auszugeben (`echo`).
|
||||
* `php://memory` um in den Arbeitsspeicher zu schreiben.
|
||||
* `php://temp` um in eine temporäre Datei zu schreiben.
|
||||
|
||||
* Der zweite Parameter nimmt die Daten entgegen die als CSV exportiert werden sollen. Folgende Typen sind erlaubt:
|
||||
* `array`
|
||||
* `Generator`
|
||||
* `Iterator`
|
||||
* `IteratorAggregate`
|
||||
|
||||
##### Konstruktor-Parameter
|
||||
|
||||
* Der erste Konstruktor-Parameter ist optional und nimmt die CSV-Konfiguration entgegen;
|
||||
siehe _Erweiterte Beispiele_ > _CSV konfigurieren_.
|
||||
|
||||
## Datei-Download erstellen
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Exporter\Csv\CsvExporter;
|
||||
|
||||
/** @var Database $db */
|
||||
$db = $container->get('Database');
|
||||
$data = $db->yieldAll('SELECT e.* FROM employees AS e');
|
||||
|
||||
$exporter = new CsvExporter();
|
||||
$resource = $exporter->exportToResource('php://memory', $data);
|
||||
rewind($resource);
|
||||
$stat = fstat($resource);
|
||||
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: must-revalidate');
|
||||
header('Content-type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="employees.csv"');
|
||||
header('Content-Length: ' . $stat['size']);
|
||||
fpassthru($resource);
|
||||
fclose($resource);
|
||||
```
|
||||
|
||||
|
||||
## Erweiterte Beispiele
|
||||
|
||||
### CSV konfigurieren
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Xentral\Components\Exporter\Csv\CsvConfig;
|
||||
use Xentral\Components\Exporter\Csv\CsvExporter;
|
||||
|
||||
$csvConfig = new CsvConfig();
|
||||
$csvConfig->setDelimiter(';');
|
||||
$csvConfig->setEnclosure('"');
|
||||
$csvConfig->setSourceCharset('UTF-8');
|
||||
$csvConfig->setTargetCharset('ISO-8859-1');
|
||||
|
||||
$exporter = new CsvExporter($csvConfig);
|
||||
$exporter->export($filePath, $data);
|
||||
```
|
||||
|
||||
### Daten zusammenführen
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Xentral\Components\Exporter\Csv\CsvExporter;
|
||||
use Xentral\Components\Exporter\Collection\DataCollection;
|
||||
|
||||
$collection = new DataCollection($headline, $employees);
|
||||
|
||||
$exporter = new CsvExporter();
|
||||
$exporter->export($filePath, $collection);
|
||||
```
|
||||
|
||||
### Daten formatieren
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Xentral\Components\Exporter\Csv\CsvExporter;
|
||||
use Xentral\Components\Exporter\Collection\FormatterCollection;
|
||||
|
||||
$formatter = new FormatterCollection($data, function ($row) {
|
||||
$row['fullname'] = $row['firstname'] . ' ' . $row['lastname'];
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
$exporter = new CsvExporter();
|
||||
$exporter->export($filePath, $formatter);
|
||||
```
|
||||
|
||||
Statt einer anonymen Funktion (`Closure`) kann ein `callable` übergeben werden.
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Adapter;
|
||||
|
||||
interface AdapterInterface extends ReaderAdapterInterface
|
||||
{
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Creates a new file using a stream
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Updates an existing file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Updates an existing file using a stream
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($path, $newpath);
|
||||
|
||||
/**
|
||||
* Copies a file to new location
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath);
|
||||
|
||||
/**
|
||||
* Deletes a single file
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* Deletes a directory and its contents
|
||||
*
|
||||
* @param string $directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteDir($directory);
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createDir($directory, array $config = []);
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Adapter;
|
||||
|
||||
use Xentral\Components\Filesystem\Exception\InvalidArgumentException;
|
||||
|
||||
final class FtpConfig
|
||||
{
|
||||
/** @var string $hostname */
|
||||
private $hostname;
|
||||
|
||||
/** @var string $username */
|
||||
private $username;
|
||||
|
||||
/** @var string $password */
|
||||
private $password;
|
||||
|
||||
/** @var string $rootDir */
|
||||
private $rootDir;
|
||||
|
||||
/** @var int $port */
|
||||
private $port;
|
||||
|
||||
/** @var int $timeout */
|
||||
private $timeout;
|
||||
|
||||
/** @var bool $passive */
|
||||
private $passive;
|
||||
|
||||
/** @var bool $ssl */
|
||||
private $ssl;
|
||||
|
||||
/**
|
||||
* @param string $hostname
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $rootDir
|
||||
* @param int $port
|
||||
* @param int $timeout
|
||||
* @param bool $passive
|
||||
* @param bool $ssl
|
||||
*/
|
||||
public function __construct(
|
||||
$hostname,
|
||||
$username,
|
||||
$password,
|
||||
$rootDir = '/',
|
||||
$port = 21,
|
||||
$timeout = 30,
|
||||
$passive = true,
|
||||
$ssl = false
|
||||
) {
|
||||
if (empty($hostname)) {
|
||||
throw new InvalidArgumentException('Hostname is empty.');
|
||||
}
|
||||
if (empty($username)) {
|
||||
throw new InvalidArgumentException('Username is empty.');
|
||||
}
|
||||
if (empty($password)) {
|
||||
throw new InvalidArgumentException('Password is empty.');
|
||||
}
|
||||
if (empty($rootDir)) {
|
||||
throw new InvalidArgumentException('Root dir is empty.');
|
||||
}
|
||||
|
||||
$this->hostname = (string)$hostname;
|
||||
$this->username = (string)$username;
|
||||
$this->password = (string)$password;
|
||||
$this->rootDir = (string)$rootDir;
|
||||
$this->port = (int)$port;
|
||||
$this->timeout = (int)$timeout;
|
||||
$this->passive = (bool)$passive;
|
||||
$this->ssl = (bool)$ssl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'host' => $this->hostname,
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
'root' => $this->rootDir,
|
||||
'port' => $this->port,
|
||||
'timeout' => $this->timeout,
|
||||
'passive' => $this->passive,
|
||||
'ssl' => $this->ssl,
|
||||
'recurseManually' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHostname()
|
||||
{
|
||||
return $this->hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function getRootDir()
|
||||
{
|
||||
return $this->rootDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPassive()
|
||||
{
|
||||
return $this->passive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSsl()
|
||||
{
|
||||
return $this->ssl;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,355 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Adapter;
|
||||
|
||||
use League\Flysystem\AdapterInterface as LeagueAdapterInterface;
|
||||
use League\Flysystem\Config as LeagueConfig;
|
||||
use League\Flysystem\Util;
|
||||
use League\Flysystem\Util\ContentListingFormatter;
|
||||
use Xentral\Components\Filesystem\PathInfo;
|
||||
|
||||
final class LeagueAdapterWrapper implements AdapterInterface
|
||||
{
|
||||
/** @var LeagueAdapterInterface $league */
|
||||
private $league;
|
||||
|
||||
/** @var bool $caseSensitive */
|
||||
private $caseSensitive = true;
|
||||
|
||||
/**
|
||||
* @param LeagueAdapterInterface $league
|
||||
*/
|
||||
public function __construct(LeagueAdapterInterface $league)
|
||||
{
|
||||
$this->league = $league;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return (bool)$this->league->has($path) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return PathInfo|false
|
||||
*/
|
||||
public function getInfo($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$metainfo = $this->league->getMetadata($path);
|
||||
if (!$metainfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$directory = Util::dirname($path);
|
||||
$metainfo['path'] = $path;
|
||||
|
||||
$formatter = new ContentListingFormatter($directory, false, $this->caseSensitive);
|
||||
$contents = $formatter->formatListing([$metainfo]);
|
||||
if (count($contents) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new PathInfo($contents[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getMetadata($path);
|
||||
if ($meta['type'] === self::TYPE_DIR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$result = $this->league->read($path);
|
||||
if (!$result || !isset($result['contents'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result['contents'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return resource|false
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getMetadata($path);
|
||||
if ($meta['type'] === self::TYPE_DIR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->league->readStream($path);
|
||||
if (!$result || !isset($result['stream'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result['stream'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
$directory = $this->normalizePath($directory);
|
||||
$contents = $this->getLeagueAdapter()->listContents($directory, $recursive);
|
||||
$formatter = new ContentListingFormatter($directory, $recursive, $this->caseSensitive);
|
||||
|
||||
return $formatter->formatListing($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->getMetadata($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getType($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getMetadata($path);
|
||||
if ($meta === false || !isset($meta['type'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $meta['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getSize($path);
|
||||
if ($meta === false || !isset($meta['size'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int)$meta['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getTimestamp($path);
|
||||
if ($meta === false || !isset($meta['timestamp'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int)$meta['timestamp'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$meta = $this->league->getMimetype($path);
|
||||
if ($meta === false || !isset($meta['mimetype'])) {
|
||||
return false;
|
||||
}
|
||||
if ($meta['type'] === 'dir') {
|
||||
return 'directory';
|
||||
}
|
||||
|
||||
return $meta['mimetype'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write($path, $contents, array $config = [])
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->write($path, $contents, new LeagueConfig($config)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file using a stream
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = [])
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->writeStream($path, $resource, new LeagueConfig($config)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update($path, $contents, array $config = [])
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->update($path, $contents, new LeagueConfig($config)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing file using a stream
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = [])
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->updateStream($path, $resource, new LeagueConfig($config)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$newpath = $this->normalizePath($newpath);
|
||||
|
||||
return $this->league->rename($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file to new location
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$newpath = $this->normalizePath($newpath);
|
||||
|
||||
return $this->league->copy($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single file
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
return $this->league->delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a directory and its contents
|
||||
*
|
||||
* @param string $directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteDir($directory)
|
||||
{
|
||||
$directory = $this->normalizePath($directory);
|
||||
|
||||
return $this->league->deleteDir($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createDir($directory, array $config = [])
|
||||
{
|
||||
$directory = $this->normalizePath($directory);
|
||||
|
||||
return $this->league->createDir($directory, new LeagueConfig($config)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LeagueAdapterInterface
|
||||
*/
|
||||
public function getLeagueAdapter()
|
||||
{
|
||||
return $this->league;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function normalizePath($path)
|
||||
{
|
||||
return Util::normalizePath($path);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Adapter;
|
||||
|
||||
use Xentral\Components\Filesystem\PathInfo;
|
||||
|
||||
interface ReaderAdapterInterface
|
||||
{
|
||||
const TYPE_DIR = 'dir';
|
||||
const TYPE_FILE = 'file';
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return PathInfo|false
|
||||
*/
|
||||
public function getInfo($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function read($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return resource|false
|
||||
*/
|
||||
public function readStream($path);
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false [dir|file]
|
||||
*/
|
||||
public function getType($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getMetadata($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getSize($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getTimestamp($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getMimetype($path);
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem;
|
||||
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices()
|
||||
{
|
||||
return [
|
||||
'FilesystemFactory' => 'onInitFilesystemFactory',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return FilesystemFactory
|
||||
*/
|
||||
public static function onInitFilesystemFactory(ContainerInterface $container)
|
||||
{
|
||||
return new FilesystemFactory($container->get('Database'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class DirNotFoundException extends RuntimeException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FileExistsException extends RuntimeException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FileNotFoundException extends RuntimeException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FilesystemException extends RuntimeException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
interface FilesystemExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class RootViolationException extends LogicException implements FilesystemExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,429 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem;
|
||||
|
||||
use Xentral\Components\Filesystem\Adapter\AdapterInterface;
|
||||
use Xentral\Components\Filesystem\Exception\DirNotFoundException;
|
||||
use Xentral\Components\Filesystem\Exception\FileExistsException;
|
||||
use Xentral\Components\Filesystem\Exception\FileNotFoundException;
|
||||
use Xentral\Components\Filesystem\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Filesystem\Exception\RootViolationException;
|
||||
|
||||
final class Filesystem implements FilesystemInterface
|
||||
{
|
||||
/** @var AdapterInterface $adapter */
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param AdapterInterface $adapter
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if file or directory exists
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
return $this->adapter->has($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return PathInfo|false
|
||||
*/
|
||||
public function getInfo($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->getInfo($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* List directory contents
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$contents = $this->adapter->listContents($directory, $recursive);
|
||||
foreach ($contents as $metainfo) {
|
||||
$result[] = PathInfo::fromMeta($metainfo);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists only directories
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listDirs($directory = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$contents = $this->adapter->listContents($directory, $recursive);
|
||||
foreach ($contents as $metainfo) {
|
||||
if ($metainfo['type'] === 'dir') {
|
||||
$result[] = PathInfo::fromMeta($metainfo);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists only files
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listFiles($directory = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$contents = $this->adapter->listContents($directory, $recursive);
|
||||
foreach ($contents as $metainfo) {
|
||||
if ($metainfo['type'] === 'file') {
|
||||
$result[] = PathInfo::fromMeta($metainfo);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* List only paths as strings
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function listPaths($directory = '', $recursive = false)
|
||||
{
|
||||
$contents = $this->adapter->listContents($directory, $recursive);
|
||||
|
||||
return array_column($contents, 'path');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string [dir|file]
|
||||
*/
|
||||
public function getType($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->getType($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filesize
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->getSize($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timestamp from last update
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->getTimestamp($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->getMimetype($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file content
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->read($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file content
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return resource|false
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->readStream($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write($path, $contents, array $config = [])
|
||||
{
|
||||
if ($this->has($path)) {
|
||||
throw new FileExistsException(sprintf('File "%s" exists already.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ($this->has($path)) {
|
||||
throw new FileExistsException(sprintf('File "%s" exists already.', $path));
|
||||
}
|
||||
if (!is_resource($resource)) {
|
||||
throw new InvalidArgumentException('Second parameter must be a resource.');
|
||||
}
|
||||
|
||||
return $this->adapter->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file or updates the file contents
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function put($path, $contents, array $config = [])
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
return $this->adapter->write($path, $contents, $config);
|
||||
}
|
||||
|
||||
return $this->adapter->update($path, $contents, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file or updates the file contents
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = [])
|
||||
{
|
||||
if (!is_resource($resource)) {
|
||||
throw new InvalidArgumentException('Second parameter must be a resource.');
|
||||
}
|
||||
|
||||
if (!$this->has($path)) {
|
||||
return $this->adapter->writeStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
return $this->adapter->updateStream($path, $resource, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @throws FileExistsException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
if ($this->has($newpath)) {
|
||||
throw new FileExistsException(sprintf('File "%s" exists already.', $newpath));
|
||||
}
|
||||
|
||||
return $this->adapter->rename($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file to new location
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @throws FileExistsException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
if ($this->has($newpath)) {
|
||||
throw new FileExistsException(sprintf('File "%s" exists already.', $newpath));
|
||||
}
|
||||
|
||||
return $this->adapter->copy($path, $newpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single file
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
if (!$this->has($path)) {
|
||||
throw new FileNotFoundException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
return $this->adapter->delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a directory and all its contents
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @throws DirNotFoundException
|
||||
* @throws RootViolationException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
$info = $this->adapter->getInfo($dirname);
|
||||
if (!$info) {
|
||||
throw new DirNotFoundException(sprintf('Directory "%s" not found.', $dirname));
|
||||
}
|
||||
if ($info->getPath() === '') {
|
||||
throw new RootViolationException('Root directory can not be deleted.');
|
||||
}
|
||||
|
||||
return $this->adapter->deleteDir($dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createDir($dirname, array $config = [])
|
||||
{
|
||||
return $this->adapter->createDir($dirname, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Filesystem\Adapter\FtpConfig;
|
||||
use Xentral\Components\Filesystem\Adapter\LeagueAdapterWrapper;
|
||||
use Xentral\Components\Filesystem\Exception\FilesystemException;
|
||||
use Xentral\Components\Filesystem\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Filesystem\Flysystem\FtpAdapterDecorator;
|
||||
use Xentral\Components\Filesystem\Flysystem\LocalAdapterDecorator;
|
||||
|
||||
final class FilesystemFactory
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $root Absolute path
|
||||
* @param array $config
|
||||
*
|
||||
* @return FilesystemInterface
|
||||
*/
|
||||
public function createLocal($root, array $config = [])
|
||||
{
|
||||
try {
|
||||
$writeFlags = isset($config['write_flags']) ? $config['write_flags'] : LOCK_EX;
|
||||
$linkHandling = isset($config['link_handling']) ? $config['link_handling'] : LocalAdapterDecorator::SKIP_LINKS;
|
||||
$permissions = isset($config['permissions']) ? $config['permissions'] : [];
|
||||
|
||||
$leagueLocalAdapter = new LocalAdapterDecorator($root, $writeFlags, $linkHandling, $permissions);
|
||||
$leagueAdapterWrapper = new LeagueAdapterWrapper($leagueLocalAdapter);
|
||||
|
||||
return new Filesystem($leagueAdapterWrapper);
|
||||
//
|
||||
} catch (Exception $e) {
|
||||
throw new FilesystemException($e->getMessage(), (int)$e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FtpConfig $config
|
||||
*
|
||||
* @return FilesystemInterface
|
||||
*/
|
||||
public function createFtp(FtpConfig $config)
|
||||
{
|
||||
try {
|
||||
$leagueFtpAdapter = new FtpAdapterDecorator($config->toArray());
|
||||
$leagueAdapterWrapper = new LeagueAdapterWrapper($leagueFtpAdapter);
|
||||
|
||||
return new Filesystem($leagueAdapterWrapper);
|
||||
//
|
||||
} catch (Exception $e) {
|
||||
throw new FilesystemException($e->getMessage(), (int)$e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilesystemInterface $filesystem
|
||||
* @param int $syncId
|
||||
*
|
||||
* @return FilesystemSyncCache
|
||||
*/
|
||||
public function createSync(FilesystemInterface $filesystem, $syncId)
|
||||
{
|
||||
try {
|
||||
if (get_class($filesystem) === FilesystemSyncCache::class) {
|
||||
throw new InvalidArgumentException('FilesystemSyncWrapper can not wrap it self.');
|
||||
}
|
||||
|
||||
return new FilesystemSyncCache($this->db, $filesystem, $syncId);
|
||||
//
|
||||
} catch (Exception $e) {
|
||||
throw new FilesystemException($e->getMessage(), (int)$e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
namespace Xentral\Components\Filesystem;
|
||||
|
||||
use Xentral\Components\Filesystem\Adapter\AdapterInterface;
|
||||
use Xentral\Components\Filesystem\Exception\DirNotFoundException;
|
||||
use Xentral\Components\Filesystem\Exception\FileExistsException;
|
||||
use Xentral\Components\Filesystem\Exception\FileNotFoundException;
|
||||
use Xentral\Components\Filesystem\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Filesystem\Exception\RootViolationException;
|
||||
|
||||
interface FilesystemInterface
|
||||
{
|
||||
/**
|
||||
* Checks if file or directory exists
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return PathInfo|false
|
||||
*/
|
||||
public function getInfo($path);
|
||||
|
||||
/**
|
||||
* List directory contents
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false);
|
||||
|
||||
// public function filterContents(array $filter = [], $directory = '', $recursive = false); // @todo
|
||||
//
|
||||
// // @todo ExtendedLocal
|
||||
// public function isReadable();
|
||||
// public function isWriteable();
|
||||
// public function getOwner();
|
||||
// public function getGroup();
|
||||
|
||||
/**
|
||||
* Lists only directories
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listDirs($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* Lists only files
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|PathInfo[]
|
||||
*/
|
||||
public function listFiles($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* List only paths as strings
|
||||
*
|
||||
* @param string $directory
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function listPaths($directory = '', $recursive = false);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string [dir|file]
|
||||
*/
|
||||
public function getType($path);
|
||||
|
||||
/**
|
||||
* Gets the filesize
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function getSize($path);
|
||||
|
||||
/**
|
||||
* Gets the timestamp from last update
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getTimestamp($path);
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function getMimetype($path);
|
||||
|
||||
/**
|
||||
* Read file content
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function read($path);
|
||||
|
||||
/**
|
||||
* Read file content
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return resource|false
|
||||
*/
|
||||
public function readStream($path);
|
||||
|
||||
/**
|
||||
* Writes to a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Writes to a new file
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws FileExistsException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Creates a file or updates the file contents
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $contents
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function put($path, $contents, array $config = []);
|
||||
|
||||
/**
|
||||
* Creates a file or updates the file contents
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $resource
|
||||
* @param array $config
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = []);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @throws FileExistsException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($path, $newpath);
|
||||
|
||||
/**
|
||||
* Copies a file to new location
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*
|
||||
* @throws FileExistsException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($path, $newpath);
|
||||
|
||||
/**
|
||||
* Deletes a single file
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* Deletes a directory and all its contents
|
||||
*
|
||||
* @param string $dirname
|
||||
*
|
||||
* @throws DirNotFoundException
|
||||
* @throws RootViolationException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteDir($dirname);
|
||||
|
||||
/**
|
||||
* Creates a directory
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function createDir($dirname, array $config = []);
|
||||
|
||||
/**
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public function getAdapter();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue