Cleanup and rename project to 幸運な偶然 - Lucky Coinkydink.

This commit is contained in:
Markus Birth 2022-02-19 22:19:07 +01:00
parent 09f47fc2d0
commit 2d127e81e1
Signed by: mbirth
GPG Key ID: A9928D7A098C3A9A
30 changed files with 50 additions and 2060 deletions

View File

@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `serendipity {at} supergarv (dot) de`. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1 +0,0 @@
Please see [Contributing/support](https://docs.s9y.org/docs/contributing/index.html).

View File

@ -1,51 +1,4 @@
# Serendipity - A reliable, secure & extensible PHP blog
幸運な偶然 - Lucky Coinkydink
===========================
[Serendipity](https://s9y.org) is a PHP-powered weblog engine that gives the user an easy way to maintain a blog. While the default package is designed for the casual blogger, Serendipity offers an expandable framework with the power for professional applications.
![Serendipity Example](https://www.onli-blogging.de/uploads/s9y_example.png)
## Installation
On most hosters, everything needed to run Serendipity should already be installed. If you install it on your custom server, install PHP >= 7.0, MySQL/MariaDB, PostgreSQL or SQLite, and Apache. Imagemagick is also useful. Upload the files from [a release archive](https://github.com/s9y/Serendipity/releases) to your webroot and visit your URL to start the installer.
For more details, please consult [the manual](https://docs.s9y.org/docs/users/getting-started/fresh-installation.html).
## Features
By default, Serendipity includes:
1. An editor to write blog entries
1. Support for trackbacks and pingbacks
1. A media library to upload images, videos and other files and add them into entries
1. Integrated anti-spam measures
1. A collection of themes that can be selected in the backend
1. A plugin management interface
1. Categories that can be applied to written blog entries
1. Groups and user management
Via plugins, additional functionality can easily be added. Some popular plugins add
1. Support for static pages, transforming Serendipity in a small CMS
1. Additional anti-spam features
1. Tags, in addition to categories
1. Markup languages like Markdown and Textile
Plugins can be added in the backend plugin interface without the need to manually upload files.
## Support
The website contains helpful [documentation](https://docs.s9y.org/docs/index.html) that might answer your questions.
Please visit [the forums](https://board.s9y.org/) for additional questions and discussions. If you are more comfortable with Github or the questions are more code related, you can also [open an issue](https://github.com/s9y). Please keep the [Code of Conduct](https://docs.s9y.org/docs/contributing/code-of-conduct.html) in mind when doing so.
You can also join us in [the Gitter channel](https://gitter.im/s9y/lobby) to get help from other users or developers.
## Development
Serendipity is developed by an independent and open [team](https://docs.s9y.org/team.html). Current development focus in on carefully modernizing the software while staying backwards compatible. If you want to contribute changes, you can send in a pull request and we will work with you to bring the changes into the software. Do that a few times and we will offer you to join the team to get direct access to the repository.
If you want to request features, you can open a thread [in the forum](https://board.s9y.org/) or open an issue on Github instead. Much of the development of Serendipity is user driven, feature requests are welcome.
Join us in [the Gitter channel](https://gitter.im/s9y/lobby) to discuss with other developers.
The [license of this project](https://github.com/s9y/Serendipity/blob/master/LICENSE) is the BSD 3-Clause license. It's a permissive license allowing free usage of the code and derived projects, including commercial and closed source usage.
Based on the [Serendipity](https://s9y.org/) PHP blogging engine.

View File

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
require_once __DIR__ . '/lib/bootstrap.php';
use Serendipity\PageGenerator;
use LuckyCoin\PageGenerator;
#if ($_REQUEST['type'] == 'trackback') die('Disabled');

View File

@ -1,6 +1,6 @@
{
"name": "serendipity/s9y",
"description": "A reliable, secure and extensible PHP blog",
"name": "mbirth/luckycoinkydink",
"description": "Blog based on Serendipity",
"repositories": [
{
"type": "composer",

2
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "93e84a595e8454cb34524e9a3adde858",
"content-hash": "7bd2cc106d78adb6b8b239de0b33a76d",
"packages": [
{
"name": "filp/whoops",

View File

@ -1,12 +1,12 @@
<?php
// Serendipity
// Lucky Coinkydink
// See LICENSE file for license information.
// FIXME: THIS IS A SHIM FOR BACKWARDS COMPATIBILITY - REMOVE WHEN NO LONGER NEEDED
use Serendipity\Database\DbFactory;
use Serendipity\Database\DbTools;
use LuckyCoin\Database\DbFactory;
use LuckyCoin\Database\DbTools;
// SQLite3 only fetches by assoc, we will emulate the other result types
define(SQLITE3_ASSOC, 0);

View File

@ -2,7 +2,7 @@
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
# All rights reserved. See LICENSE file for licensing details
use Serendipity\ContentCache;
use LuckyCoin\ContentCache;
if (IN_serendipity !== true) {
die ("Don't hack!");

View File

@ -2,7 +2,7 @@
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
# All rights reserved. See LICENSE file for licensing details
use Serendipity\ContentCache;
use LuckyCoin\ContentCache;
if (IN_serendipity !== true) {
die ("Don't hack!");

View File

@ -2,7 +2,7 @@
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
# All rights reserved. See LICENSE file for licensing details
use Serendipity\ContentCache;
use LuckyCoin\ContentCache;
if (IN_serendipity !== true) {
die ("Don't hack!");

View File

@ -1,10 +1,10 @@
<?php
// Serendipity
// Lucky Coinkydink
// See LICENSE file for license information.
// TODO: THIS FILE IS FOR BACKWARDS COMPATIBILITY - REMOVE WHEN NO LONGER NEEDED
use Serendipity\PageGenerator;
use LuckyCoin\PageGenerator;
$pg = new PageGenerator();
$pg->render();

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
require_once __DIR__ . '/lib/bootstrap.php';
use Serendipity\Routing;
use LuckyCoin\Routing;
// We need to set this to return a 200 since we use .htaccess ErrorDocument
// rules to handle archives.

View File

@ -1,9 +1,9 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity;
namespace LuckyCoin;
use ArrayAccess;

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity;
namespace LuckyCoin;
use Serendipity\ConfigContainer;
use LuckyCoin\ConfigContainer;
use voku\cache\Cache;
use voku\cache\CacheAdapterAutoManager;
use voku\cache\AdapterArray;

View File

@ -1,9 +1,9 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
abstract class DbAbstract
{

View File

@ -1,9 +1,9 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
use Exception;
@ -11,7 +11,7 @@ class DbFactory
{
private static $db_instance = null;
public static function createFromConfig(&$serendipity): \Serendipity\Database\DbAbstract
public static function createFromConfig(&$serendipity): \LuckyCoin\Database\DbAbstract
{
if (self::$db_instance !== null) {
// Already instantiated - return it
@ -19,15 +19,9 @@ class DbFactory
}
$config2class = [
'mysql' => 'Mysqli',
'mysqli' => 'Mysqli',
'pdo-postgres' => 'PdoPostgres',
'pdo-sqlite' => 'PdoSqlite',
'postgres' => 'Postgres',
'sqlite' => 'Sqlite',
'sqlite3' => 'Sqlite3',
'sqlite3oo' => 'Sqlite3oo',
'sqlrelay' => 'SqlRelay',
];
if (!array_key_exists($serendipity['dbType'], $config2class)) {
@ -35,7 +29,7 @@ class DbFactory
}
// Name of database class
$dbClass = '\\Serendipity\\Database\\' . $config2class[$serendipity['dbType']] . 'Database';
$dbClass = '\\LuckyCoin\\Database\\' . $config2class[$serendipity['dbType']] . 'Database';
self::$db_instance = new $dbClass($serendipity);
return self::$db_instance;

View File

@ -1,9 +1,9 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
class DbTools
{

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
use Serendipity\Database\DbAbstract;
use LuckyCoin\Database\DbAbstract;
use Exception;
class MysqliDatabase extends DbAbstract

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
use Serendipity\Database\DbAbstract;
use LuckyCoin\Database\DbAbstract;
use PDO;
class PdoPostgresDatabase extends DbAbstract

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity\Database;
namespace LuckyCoin\Database;
use Serendipity\Database\DbAbstract;
use LuckyCoin\Database\DbAbstract;
use PDO;
class PdoSqliteDatabase extends DbAbstract

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity;
namespace LuckyCoin;
use Serendipity\ConfigContainer;
use LuckyCoin\ConfigContainer;
class PageGenerator
{

View File

@ -1,12 +1,12 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
namespace Serendipity;
namespace LuckyCoin;
use Serendipity\ConfigContainer;
use Serendipity\PageGenerator;
use LuckyCoin\ConfigContainer;
use LuckyCoin\PageGenerator;
class Routing
{

View File

@ -1,352 +0,0 @@
<?php
// Serendipity
// See LICENSE file for license information.
namespace Serendipity\Database;
use Serendipity\Database\DbAbstract;
class PostgresDatabase extends DbAbstract
{
/**
* Tells the DB Layer to start a DB transaction.
*
* @access public
*/
public function beginTransaction()
{
$this->query('begin work');
}
/**
* Tells the DB Layer to end a DB transaction.
*
* @access public
* @param boolean If true, perform the query. If false, rollback.
*/
public function endTransaction(bool $commit)
{
if ($commit) {
$this->query('commit');
} else {
$this->query('rollback');
}
}
/**
* Assemble and return SQL condition for a "IN (...)" clause
*
* @access public
* @param string table column name
* @param array referenced array of values to search for in the "IN (...)" clause
* @param string condition of how to associate the different input values of the $search_ids parameter
* @return string resulting SQL string
*/
public function inSql($col, &$search_ids, $type = ' OR ')
{
return $col . " IN (" . implode(', ', $search_ids) . ")";
}
/**
* Connect to the configured Database
*
* @access public
* @return resource connection handle
*/
public function connect()
{
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
$function = 'pg_pconnect';
} else {
$function = 'pg_connect';
}
$host = $port = '';
if (strlen($this->db_hostname)) {
if (false !== strstr($this->db_hostname, ':')) {
$tmp = explode(':', $this->db_hostname);
$host = "host={$tmp[0]} ";
$port = "port={$tmp[1]} ";
} else {
$host = "host={$this->db_hostname} ";
}
}
$this->db_conn = $function(
sprintf(
'%sdbname=%s user=%s password=%s',
"$host$port",
$this->db_name,
$this->db_username,
$this->db_password
)
);
return $this->db_conn;
}
/**
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
*/
public function escapeString($string): string
{
return pg_escape_string($string);
}
/**
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
*
* @access public
* @param int Number of the first row to return data from
* @param int Number of rows to return
* @return string SQL string to pass to a LIMIT statement
*/
public function limit($start, $offset)
{
return $offset . ', ' . $start;
}
/**
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
*
* @access public
* @param SQL string of a LIMIT option
* @return SQL string containing a full LIMIT statement
*/
public function limitSql($limitstring)
{
$limit_split = explode(',', $limitstring);
if (count($limit_split) > 1) {
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
} else {
$limit = ' LIMIT ' . $limit_split[0];
}
return $limit;
}
/**
* Returns the number of affected rows of a SQL query
*
* @access public
* @return int Number of affected rows
*/
public function affectedRows()
{
return pg_affected_rows($this->serendipity['dbLastResult']);
}
/**
* Returns the number of updated rows in a SQL query
*
* @access public
* @return int Number of updated rows
*/
public function updatedRows()
{
// it is unknown whether pg_affected_rows returns number of rows
// UPDATED or MATCHED on an UPDATE statement.
return pg_affected_rows($this->serendipity['dbLastResult']);
}
/**
* Returns the number of matched rows in a SQL query
*
* @access public
* @return int Number of matched rows
*/
public function matchedRows()
{
// it is unknown whether pg_affected_rows returns number of rows
// UPDATED or MATCHED on an UPDATE statement.
return pg_affected_rows($this->serendipity['dbLastResult']);
}
/**
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
*
* @access public
* @param string Name of the table to get a INSERT ID for
* @param string Name of the column to get a INSERT ID for
* @return int Value of the auto-increment column
*/
public function insertId($table = '', $id = '')
{
if (empty($table) || empty($id)) {
// BC - will/should never be called with empty parameters!
return pg_last_oid($this->serendipity['dbLastResult']);
} else {
$query = "SELECT currval('{$this->db_prefix}{$table}_{$id}_seq'::text) AS {$id}";
$res = pg_query($this->db_conn, $query);
if (pg_num_rows($res)) {
$insertId = pg_fetch_array($res, 0, PGSQL_ASSOC);
return $insertId[$id];
} else {
return pg_last_oid($this->serendipity['dbLastResult']); // BC - should not happen!
}
}
}
/**
* Perform a DB Layer SQL query.
*
* This function returns values dependin on the input parameters and the result of the query.
* It can return:
* false or a string if there was an error (depends on $expectError),
* true if the query succeeded but did not generate any rows
* array of field values if it returned a single row and $single is true
* array of array of field values if it returned row(s) [stacked array]
*
* @access public
* @param string SQL query to execute
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
* @param boolean If true, errors will be reported. If false, errors will be ignored.
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
* @return mixed Returns the result of the SQL query, depending on the input parameters
*/
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
{
$type_map = array(
'assoc' => PGSQL_ASSOC,
'num' => PGSQL_NUM,
'both' => PGSQL_BOTH,
'true' => true,
'false' => false
);
if (!isset($this->serendipity['dbPgsqlOIDS'])) {
$this->serendipity['dbPgsqlOIDS'] = true;
@$this->query('SET default_with_oids = true', true, 'both', false, false, false, true);
}
if (!$expectError && ($reportErr || !$this->serendipity['production'])) {
$this->serendipity['dbLastResult'] = pg_query($this->db_conn, $sql);
} else {
$this->serendipity['dbLastResult'] = @pg_query($this->db_conn, $sql);
}
if (!$this->serendipity['dbLastResult']) {
if (!$expectError && !$this->serendipity['production']) {
print "<span class='msg_error'>Error in $sql</span>";
print pg_last_error($this->db_conn) . "<BR/>\n";
if (function_exists('debug_backtrace')) {
highlight_string(var_export(debug_backtrace(), 1));
}
print "<pre>$sql</pre>";
}
return $type_map['false'];
}
if ($this->serendipity['dbLastResult'] === true) {
return $type_map['true'];
}
$result_type = $type_map[$result_type];
$n = pg_num_rows($this->serendipity['dbLastResult']);
switch ($n) {
case 0:
if ($single) {
return $type_map['false'];
}
return $type_map['true'];
case 1:
if ($single) {
return pg_fetch_array($this->serendipity['dbLastResult'], 0, $result_type);
}
default:
$rows = array();
for ($i = 0; $i < $n; $i++) {
if (!empty($assocKey)) {
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
$row = pg_fetch_array($this->serendipity['dbLastResult'], $i, $result_type);
if (empty($assocVal)) {
$rows[$row[$assocKey]] = $row;
} else {
$rows[$row[$assocKey]] = $row[$assocVal];
}
} else {
$rows[] = pg_fetch_array($this->serendipity['dbLastResult'], $i, $result_type);
}
}
return $rows;
}
}
/**
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
*
* @access public
* @param string SQL query with template variables to convert
* @return resource SQL resource handle of the executed query
*/
public function schemaImport($query)
{
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
'{FULLTEXT}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
static $replace = array('SERIAL', 'primary key', '', '', 'BOOLEAN NOT NULL', 'int2',
'int4', 'int4', 'int4', '', 'text');
if (stristr($query, '{FULLTEXT_MYSQL}')) {
return true;
}
$query = trim(str_replace($search, $replace, $query));
if ($query[0] == '@') {
// Errors are expected to happen (like duplicate index creation)
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
} else {
return $this->query($query);
}
}
/**
* Try to connect to the configured Database (during installation)
*
* @access public
* @param array input configuration array, holding the connection info
* @param array referenced array which holds the errors that might be encountered
* @return boolean return true on success, false on error
*/
public function probe($hash, &$errs)
{
if (!function_exists('pg_connect')) {
$errs[] = 'No PostgreSQL extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
return false;
}
$this->db_conn = pg_connect(
sprintf(
'%sdbname=%s user=%s password=%s',
strlen($hash['dbHost']) ? ('host=' . $hash['dbHost'] . ' ') : '',
$hash['dbName'],
$hash['dbUser'],
$hash['dbPass']
)
);
if (!$this->db_conn) {
$errs[] = 'Could not connect to database; check your settings.';
return false;
}
return true;
}
/**
* Returns the SQL code used for concatenating strings
*
* @access public
* @param string Input string/column to concatenate
* @return string SQL parameter
*/
public function concat($string)
{
return '(' . str_replace(', ', '||', $string) . ')';
}
}

View File

@ -1,548 +0,0 @@
<?php
// Serendipity
// See LICENSE file for license information.
namespace Serendipity\Database;
use Serendipity\Database\DbAbstract;
class SqlRelayDatabase extends DbAbstract
{
protected $db_cursor;
/**
* Tells the DB Layer to start a DB transaction.
*
* @access public
*/
public function beginTransaction()
{
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case 'mysql':
$sqlstr = 'start transaction';
break;
case 'postgresql':
$sqlstr = 'begin work';
break;
case 'sqlite':
$sqlstr = 'begin transaction';
break;
default:
$sqlstr = 'start transaction';
break;
}
$this->query($sqlstr);
}
/**
* Tells the DB Layer to end a DB transaction.
*
* @access public
* @param boolean $commit If true, perform the query. If false, rollback.
*/
public function endTransaction(bool $commit)
{
if ($commit) {
sqlrcon_commit($this->db_conn);
} else {
sqlrcon_rollback($this->db_conn);
}
}
/**
* Assemble and return SQL condition for a "IN (...)" clause
*
* @access public
* @param string table column name
* @param array referenced array of values to search for in the "IN (...)" clause
* @param string condition of how to associate the different input values of the $search_ids parameter
* @return string resulting SQL string
*/
public function inSql($col, &$search_ids, $type = ' OR ')
{
if (sqlrcon_identify($this->db_conn) == "sqlite") {
$sql = array();
if (!is_array($search_ids)) {
return false;
}
foreach ($search_ids as $id) {
$sql[] = $col . ' = ' . $id;
}
$cond = '(' . implode($type, $sql) . ')';
return $cond;
} else {
return $col . " IN (" . implode(', ', $search_ids) . ")";
}
}
/**
* Perform a DB Layer SQL query.
*
* This function returns values dependin on the input parameters and the result of the query.
* It can return:
* false or a string if there was an error (depends on $expectError),
* true if the query succeeded but did not generate any rows
* array of field values if it returned a single row and $single is true
* array of array of field values if it returned row(s) [stacked array]
*
* @access public
* @param string SQL query to execute
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
* @param boolean If true, errors will be reported. If false, errors will be ignored.
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
* @return mixed Returns the result of the SQL query, depending on the input parameters
*/
protected function generateResultset($cursor, $result_type = 'sqlr_BOTH')
{
$return_row = array();
for ($r_row = 0, $max_r = sqlrcur_rowCount($cursor) ; $r_row < $max_r ; $r_row++) {
for ($r_col=0, $max_rc = sqlrcur_colCount($cursor); $r_col < $max_rc ; $r_col++) {
if ($result_type == 'sqlr_ASSOC') {
$return_row[sqlrcur_getColumnName($cursor, $r_col)] = sqlrcur_getField($cursor, $r_row, $r_col);
} else {
$return_row[$r_row][$r_col] = sqlrcur_getField($cursor, $r_row,$r_col);
$return_row[$r_row][sqlrcur_getColumnName($cursor, $r_col)] = $return_row[$r_row][$r_col];
}
}
}
return $return_row;
}
public function &query($sql, $single = false, $result_type = "both", $reportErr = false, $assocKey = false, $assocVal = false, $expectError = false)
{
$type_map = array(
'assoc' => sqlr_ASSOC,
'num' => sqlr_NUM,
'both' => sqlr_BOTH,
'true' => true,
'false' => false
);
static $benchmark = false;
// highlight_string(var_export($sql, 1));
if (!is_resource($this->db_conn)) {
return false;
}
$cur = sqlrcur_alloc($this->db_conn);
$this->db_cursor = $cur;
if ($benchmark) {
$start = microtime(true);
}
if ($expectError) {
$c = sqlrcur_sendQuery($cur, $sql);
} else {
$c = sqlrcur_sendQuery($cur, $sql);
}
if ($benchmark) {
$end = microtime(true);
$cur = sqlrcur_alloc($this->db_conn);
$sql_b = "INSERT INTO BLOGLOG (request, timestamp, sql, exec_time, ip) VALUES ('" . $this->escapeString($_SERVER['REQUEST_URI']) . "', NOW(), '" . $this->escapeString($sql) . "', '" . (number_format($end-$start, 10)) . "', '" . $this->escapeString($_SERVER['REMOTE_ADDR']) . "')";
$c = sqlrcur_sendQuery($cur, $sql_b);
$psql = $sql;
$psql = preg_replace('@[0-9]{10}@', 'TIMESTAMP', $psql);
$sql_U = "UPDATE BLOGLOG_TOTAL SET counter = counter + 1 WHERE sql = '" . $this->escapeString($psql) . "'";
$c = sqlrcur_sendQuery($cur, $sql_U);
if (sqlrcur_affectedRows() < 1) {
$sql_i = "INSERT INTO BLOGLOG_TOTAL (sql, counter) VALUES ('" . $this->escapeString($psql) . "', 1)";
$c = sqlrcur_sendQuery($cur,$sql_i);
}
}
if (!$expectError && sqlrcur_errorMessage($cur) != '') {
$msg = '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
return $msg;
}
if (!$c) {
if (!$expectError && !$this->serendipity['production']) {
print '<pre>' . serendipity_specialchars($sql) . '</pre> / ' . serendipity_specialchars(sqlrcur_errorMessage($cur));
if (function_exists('debug_backtrace') && $reportErr == true) {
highlight_string(var_export(debug_backtrace(), 1));
}
}
return $type_map['false'];
}
if ($c === true) {
return $type_map['true'];
}
$result_type = $type_map[$result_type];
switch (sqlrcur_rowCount($cur)) {
case 0:
if ($single) {
return $type_map['false'];
}
return $type_map['true'];
case 1:
if ($single) {
$row = $this->generateResultset($cur, $result_type);
if ($result_type != 'sqlr_ASSOC') {
$row=$row[0];
}
return $row;
}
default:
if ($single) {
return $this->generateResultset($cur, $result_type);
}
$row = $this->generateResultset($cur);
$rows = array();
for ($idx=0, $idxc = count($row); $idx < $idxc ; $idx++) {
if (!empty($assocKey)) {
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
if (empty($assocVal)) {
$rows[$assocKey] = $row[$idx][$assocKey];
} else {
$rows[$row[$idx][$assocKey]] = $row[$idx][$assocVal];
}
} else {
$rows = $row;
}
}
return $rows;
}
}
/**
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
*
* @access public
* @return int Value of the auto-increment column
*
* If you insert multiple rows using a single INSERT statement,
* LAST_INSERT_ID() returns the value generated for the first inserted row only.
*/
public function insertId($table = '', $id = '')
{
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case 'mysql':
$sqlstr='SELECT LAST_INSERT_ID()';
break;
case 'postgresql':
$sqlstr = "SELECT currval('{$this->db_prefix}{$table}_{$id}_seq'::text) AS {$id}";
break;
case 'sqlite':
$sqlstr = 'SELECT last_insert_rowid()';
break;
default:
$sqlstr = 'SELECT LAST_INSERT_ID()';
}
$row = $this->query($sqlstr, true);
return $row[0];
}
/**
* Returns the number of affected rows of a SQL query
*
* @access public
* @return int Number of affected rows
*/
public function affectedRows()
{
/* int sqlrcur_affectedRows(int sqlrcurref)
* Returns the number of rows that were updated, inserted or deleted by the query.
* Not all databases support this call.
* Don't use it for applications which are designed to be portable across databases.
* -1 is returned by databases which don't support this option.
*/
return sqlrcur_affectedRows($this->db_cursor);
}
/**
* Returns the number of updated rows in a SQL query
*
* @access public
* @return int Number of updated rows
*/
public function updatedRows()
{
/*
preg_match(
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
mysql_info(),
$arr);
// mysql_affected_rows returns 0 if rows were matched but not changed.
// mysql_info returns rows matched AND rows changed
return $arr[2];
*/
return $this->affectedRows();
}
/**
* Returns the number of matched rows in a SQL query
*
* @access public
* @return int Number of matched rows
*/
public function matchedRows()
{
/*
preg_match(
"/^[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/",
mysql_info(),
$arr);
// mysql_affected_rows returns 0 if rows were matched but not changed.
// mysql_info returns rows matched AND rows changed
return $arr[1];
*/
return $this->affectedRows();
}
/**
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
*/
public function escapeString($string): string
{
static $search = array("\x00", '%', "'", '\"');
static $replace = array('%00', '%25', "''", '\\\"');
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case 'mysql':
if (function_exists('mysql_real_escape_string')) {
$rtnstr = mysql_real_escape_string($str, $this->db_conn);
} else {
$rtnstr = addslashes($str);
}
break;
case 'postgresql':
if (function_exists('pg_escape_string')) {
$rtnstr = pg_escape_string($str);
} else {
$rtnstr = addslashes($str);
}
break;
case 'sqlite':
$rtnstr = str_replace($search, $replace, $str);
break;
default:
$rtnstr = addslashes($str);
}
return $rtnstr;
}
/**
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
*
* @access public
* @param int Number of the first row to return data from
* @param int Number of rows to return
* @return string SQL string to pass to a LIMIT statement
*/
public function limit($start, $offset)
{
return $start . ', ' . $offset;
}
/**
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
*
* @access public
* @param SQL string of a LIMIT option
* @return SQL string containing a full LIMIT statement
*/
public function limitSql($limitstring)
{
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case "mysql":
return ' LIMIT ' . $limitstring;
case "postgresql":
$limit_split = explode(',', $limitstring);
if (count($limit_split) > 1) {
$limit = ' LIMIT ' . $limit_split[0] . ' OFFSET ' . $limit_split[1];
} else {
$limit = ' LIMIT ' . $limit_split[0];
}
return $limit;
default:
return ' LIMIT ' . $limitstring;
}
}
/**
* Connect to the configured Database
*
* @access public
* @return resource connection handle
*/
public function connect()
{
if (isset($this->db_conn)) {
return $this->db_conn;
}
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
$function = 'sqlrcon_alloc';
} else {
$function = 'sqlrcon_alloc';
}
#$this->db_hostname="localhost:9000"
$dbHostPort = explode(":", $this->db_hostname);
$this->db_conn = $function($dbHostPort[0], $dbHostPort[1], "", $this->db_username, $this->db_password, 0, 1);
sqlrcon_debugOff($this->db_conn);
if( sqlrcon_identify($this->db_conn) == "mysql") {
$this->reconnect();
}
return $this->db_conn;
}
public function reconnect()
{
if (isset($this->serendipity['dbCharset'])) {
$this->query("SET NAMES " . $this->serendipity['dbCharset']);
@define('SQL_CHARSET_INIT', true);
} elseif (defined('SQL_CHARSET') && $this->serendipity['dbNames'] && !defined('SQL_CHARSET_INIT')) {
$this->query("SET NAMES " . SQL_CHARSET);
}
}
/**
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
*
* @access public
* @param string SQL query with template variables to convert
* @return resource SQL resource handle of the executed query
*/
public function schemaImport($query)
{
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case "mysql":
static $search = array('{AUTOINCREMENT}', '{PRIMARY}',
'{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{TEXT}');
static $replace = array('int(11) not null auto_increment', 'primary key',
'unsigned' , 'FULLTEXT', 'FULLTEXT', 'enum (\'true\', \'false\') NOT NULL default \'true\'', 'LONGTEXT');
static $is_utf8 = null;
if ($is_utf8 === null) {
$search[] = '{UTF_8}';
if ((isset($_POST['charset']) && $_POST['charset'] == 'UTF-8/') ||
$this->serendipity['charset'] == 'UTF-8/' ||
$this->serendipity['POST']['charset'] == 'UTF-8/' ||
LANG_CHARSET == 'UTF-8' ) {
$replace[] = '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */';
} else {
$replace[] = '';
}
}
break;
case "postgresql":
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}',
'{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', 'int(1)', 'int(10)', 'int(11)', 'int(4)', '{UTF_8}', '{TEXT}');
static $replace = array('SERIAL', 'primary key', '',
'', '', 'BOOLEAN NOT NULL', 'int2', 'int4', 'int4', 'int4', '', 'text');
break;
case "sqlite":
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{FULLTEXT_MYSQL}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
break;
default:
return true;
}
$query = trim(str_replace($search, $replace, $query));
if ($query{0} == '@') {
// Errors are expected to happen (like duplicate index creation)
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
} else {
return $this->query($query);
}
}
/**
* Try to connect to the configured Database (during installation)
*
* @access public
* @param array input configuration array, holding the connection info
* @param array referenced array which holds the errors that might be encountered
* @return boolean return true on success, false on error
*/
public function probe($hash, &$errs)
{
$c = null;
if (!function_exists('sqlrcon_alloc')) {
$errs[] = 'No sql_relay extension found. Please check your webserver installation or contact your systems administrator regarding this problem.';
return false;
}
if (!($c=@sqlrcon_alloc($hash['dbHost'], '', '', $hash['dbUser'], $hash['dbPass'], 0, 5))) {
$errs[] = 'Could not connect to database; check your settings.';
return false;
}
$this->db_conn = $c;
/* which db should be used is defined in sqlrelay.conf */
return true;
}
/**
* Returns the SQL code used for concatenating strings
*
* @access public
* @param string Input string/column to concatenate
* @return string SQL parameter
*/
public function concat($string)
{
$databaseType = sqlrcon_identify($this->db_conn);
switch ($databaseType) {
case 'mysql':
return 'concat(' . $string . ')';
case 'postgresql':
return '(' . str_replace(', ', '||', $string) . ')';
case 'sqlite':
return 'concat(' . $string . ')';
default:
return 'concat(' . $string . ')';
}
}
}

View File

@ -1,386 +0,0 @@
<?php
// Serendipity
// See LICENSE file for license information.
namespace Serendipity\Database;
use Serendipity\Database\DbAbstract;
class Sqlite3Database extends DbAbstract
{
// SQLite3 only fetches by assoc, we will emulate the other result types
public const SQLITE3_ASSOC = 0;
public const SQLITE3_NUM = 1;
public const SQLITE3_BOTH = 2;
/**
* Tells the DB Layer to start a DB transaction.
*
* @access public
*/
public function beginTransaction()
{
$this->query('begin transaction');
}
/**
* Tells the DB Layer to end a DB transaction.
*
* @access public
* @param boolean If true, perform the query. If false, rollback.
*/
public function endTransaction(bool $commit)
{
if ($commit) {
$this->query('commit transaction');
} else {
$this->query('rollback transaction');
}
}
/**
* Connect to the configured Database
*
* @access public
* @return resource connection handle
*/
public function connect()
{
if (isset($this->db_conn)) {
return $this->db_conn;
}
// SQLite3 doesn't support persistent connections
$this->db_conn = sqlite3_open((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $this->serendipity['serendipityPath']) . $this->db_name . '.db');
return $this->db_conn;
}
/**
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
*/
public function escapeString($string): string
{
static $search = array("\x00", '%', "'", '\"');
static $replace = array('%00', '%25', "''", '\\\"');
return str_replace($search, $replace, $string);
}
/**
* Returns the number of affected rows of a SQL query
*
* @access public
* @return int Number of affected rows
*/
public function affectedRows()
{
return sqlite3_changes($this->db_conn);
}
/**
* Returns the number of updated rows in a SQL query
*
* @access public
* @return int Number of updated rows
*/
public function updatedRows()
{
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
return $this->affectedRows();
}
/**
* Returns the number of matched rows in a SQL query
*
* @access public
* @return int Number of matched rows
*/
public function matchedRows()
{
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
return $this->affectedRows;
}
/**
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
*
* @access public
* @return int Value of the auto-increment column
*/
public function insertId()
{
return sqlite3_last_insert_rowid($this->db_conn);
}
/**
* Parse result arrays into expected format for further operations
*
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
* Yeah. This sucks. Don't tell me!
*
* @access private
* @param resource The row resource handle
* @param int Bitmask to tell whether to fetch numerical/associative arrays
* @return array Propper array containing the resource results
*/
protected function fetchArray($res, $type = self::SQLITE3_BOTH)
{
static $search = array('%00', '%25');
static $replace = array("\x00", '%');
$row = sqlite3_fetch_array($res);
if (!is_array($row)) {
return $row;
}
/* strip any slashes, correct fieldname */
foreach ($row as $i => $v) {
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
// instead of just 'id' and 'text' like in mysql/postgresql extension.
// To fix that, we use a preg-regex; but that is quite performance costy.
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
// or the sqlite extension may get fixed. :-)
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
}
if ($type == self::SQLITE3_NUM) {
$frow = array();
} else {
$frow = $row;
}
if ($type != self::SQLITE3_ASSOC) {
$i = 0;
foreach ($row as $k => $v) {
$frow[$i] = $v;
$i++;
}
}
return $frow;
}
/**
* Assemble and return SQL condition for a "IN (...)" clause
*
* @access public
* @param string table column name
* @param array referenced array of values to search for in the "IN (...)" clause
* @param string condition of how to associate the different input values of the $search_ids parameter
* @return string resulting SQL string
*/
public function inSql($col, &$search_ids, $type = ' OR ')
{
$sql = array();
if (!is_array($search_ids)) {
return false;
}
foreach ($search_ids as $id) {
$sql[] = $col . ' = ' . $id;
}
$cond = '(' . implode($type, $sql) . ')';
return $cond;
}
/**
* Perform a DB Layer SQL query.
*
* This function returns values dependin on the input parameters and the result of the query.
* It can return:
* false or a string if there was an error (depends on $expectError),
* true if the query succeeded but did not generate any rows
* array of field values if it returned a single row and $single is true
* array of array of field values if it returned row(s) [stacked array]
*
* @access public
* @param string SQL query to execute
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
* @param boolean If true, errors will be reported. If false, errors will be ignored.
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
* @return mixed Returns the result of the SQL query, depending on the input parameters
*/
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
{
$type_map = array(
'assoc' => self::SQLITE3_ASSOC,
'num' => self::SQLITE3_NUM,
'both' => self::SQLITE3_BOTH,
'true' => true,
'false' => false
);
static $debug = false;
if ($debug) {
// Open file and write directly. In case of crashes, the pointer needs to be killed.
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
fclose($fp);
}
if ($reportErr && !$expectError) {
$res = sqlite3_query($this->db_conn, $sql);
} else {
$res = @sqlite3_query($this->db_conn, $sql);
}
if (!$res) {
if (!$expectError && !$this->serendipity['production']) {
var_dump($res);
var_dump($sql);
$msg = "problem with query";
return $msg;
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
fclose($fp);
}
return $type_map['false'];
}
if ($res === true) {
return $type_map['true'];
}
$rows = array();
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
if (!empty($assocKey)) {
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
if (empty($assocVal)) {
$rows[$row[$assocKey]] = $row;
} else {
$rows[$row[$assocKey]] = $row[$assocVal];
}
} else {
$rows[] = $row;
}
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
fclose($fp);
}
if ($single && count($rows) == 1) {
return $rows[0];
}
if (count($rows) == 0) {
if ($single) {
return $type_map['false'];
}
return $type_map['true'];
}
return $rows;
}
/**
* Try to connect to the configured Database (during installation)
*
* @access public
* @param array input configuration array, holding the connection info
* @param array referenced array which holds the errors that might be encountered
* @return boolean return true on success, false on error
*/
public function probe($hash, &$errs)
{
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
if (!function_exists('sqlite3_open')) {
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
return false;
}
if (defined('S9Y_DATA_PATH')) {
// Shared installations!
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
} else {
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
}
$this->db_conn = sqlite3_open($dbfile);
if ($this->db_conn) {
return true;
}
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
return false;
}
/**
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
*
* @access public
* @param string SQL query with template variables to convert
* @return resource SQL resource handle of the executed query
*/
public function schemaImport($query)
{
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
static $replace = array('INTEGER AUTOINCREMENT', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
if (stristr($query, '{FULLTEXT_MYSQL}')) {
return true;
}
$query = trim(str_replace($search, $replace, $query));
$query = str_replace('INTEGER AUTOINCREMENT PRIMARY KEY', 'INTEGER PRIMARY KEY AUTOINCREMENT', $query);
if ($query[0] == '@') {
// Errors are expected to happen (like duplicate index creation)
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
} else {
return $this->query($query);
}
}
/**
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
*
* @access public
* @param int Number of the first row to return data from
* @param int Number of rows to return
* @return string SQL string to pass to a LIMIT statement
*/
public function limit($start, $offset)
{
return $start . ', ' . $offset;
}
/**
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
*
* @access public
* @param SQL string of a LIMIT option
* @return SQL string containing a full LIMIT statement
*/
public function limitSql($limitstring)
{
return ' LIMIT ' . $limitstring;
}
/**
* Returns the SQL code used for concatenating strings
*
* @access public
* @param string Input string/column to concatenate
* @return string SQL parameter
*/
public function concat($string)
{
return 'concat(' . $string . ')';
}
}

View File

@ -1,248 +0,0 @@
<?php
// Serendipity
// See LICENSE file for license information.
namespace Serendipity\Database;
use Serendipity\Database\Sqlite3Database;
class Sqlite3ooDatabase extends Sqlite3Database
{
/**
* Connect to the configured Database
*
* @access public
* @return resource connection handle
*/
public function connect()
{
if (isset($this->db_conn)) {
return $this->db_conn;
}
// SQLite3 doesn't support persistent connections
$this->db_conn = new SQLite3((defined('S9Y_DATA_PATH') ? S9Y_DATA_PATH : $this->serendipity['serendipityPath']) . $this->db_name . '.db');
return $this->db_conn;
}
/**
* Returns the number of affected rows of a SQL query
*
* @access public
* @return int Number of affected rows
*/
public function affectedRows()
{
return $this->db_conn->changes();
}
/**
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
*
* @access public
* @return int Value of the auto-increment column
*/
public function insertId()
{
return $this->db_conn->lastInsertRowID();
}
/**
* Parse result arrays into expected format for further operations
*
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
* Yeah. This sucks. Don't tell me!
*
* @access private
* @param resource The row resource handle
* @param int Bitmask to tell whether to fetch numerical/associative arrays
* @return array Propper array containing the resource results
*/
public function fetchArray($res, $type = self::SQLITE3_BOTH)
{
static $search = array('%00', '%25');
static $replace = array("\x00", '%');
try {
$row = $res->fetchArray();
} catch (Exception $e) {
$row = false;
echo "SQLITE-EXCEPTION: " . $e->getMessage() . "\n";
}
if (!is_array($row)) {
return $row;
}
/* strip any slashes, correct fieldname */
foreach ($row as $i => $v) {
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
// instead of just 'id' and 'text' like in mysql/postgresql extension.
// To fix that, we use a preg-regex; but that is quite performance costy.
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
// or the sqlite extension may get fixed. :-)
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
}
if ($type == self::SQLITE3_NUM) {
$frow = array();
} else {
$frow = $row;
}
if ($type != self::SQLITE3_ASSOC) {
$i = 0;
foreach ($row as $k => $v) {
$frow[$i] = $v;
$i++;
}
}
return $frow;
}
/**
* Perform a DB Layer SQL query.
*
* This function returns values dependin on the input parameters and the result of the query.
* It can return:
* false or a string if there was an error (depends on $expectError),
* true if the query succeeded but did not generate any rows
* array of field values if it returned a single row and $single is true
* array of array of field values if it returned row(s) [stacked array]
*
* @access public
* @param string SQL query to execute
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
* @param boolean If true, errors will be reported. If false, errors will be ignored.
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignroed on DUPLICATE INDEX queries and the likes)
* @return mixed Returns the result of the SQL query, depending on the input parameters
*/
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
{
$type_map = array(
'assoc' => self::SQLITE3_ASSOC,
'num' => self::SQLITE3_NUM,
'both' => self::SQLITE3_BOTH,
'true' => true,
'false' => false
);
static $debug = false;
if ($debug) {
// Open file and write directly. In case of crashes, the pointer needs to be killed.
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
fclose($fp);
}
if ($reportErr && !$expectError) {
$res = $this->db_conn->query($sql);
} else {
$res = @$this->db_conn->query($sql);
}
if (!$res) {
if (!$expectError && !$this->serendipity['production']) {
var_dump($res);
var_dump($sql);
$msg = "problem with query";
return $msg;
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
fclose($fp);
}
return $type_map['false'];
}
if (!preg_match('@^SELECT@imsU', trim($sql))) {
// Everything that is not SELECT will not return rows.
// SQLite3 OO will always return an object though.
if ($this->db_conn->lastErrorCode() > 0) {
echo "SQLITE-ERROR: " . $this->db_conn->lastErrorMsg() . "<br />\n";
return $type_map['false'];
} else {
return $type_map['true'];
}
}
$rows = array();
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
if (!empty($assocKey)) {
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
if (empty($assocVal)) {
$rows[$row[$assocKey]] = $row;
} else {
$rows[$row[$assocKey]] = $row[$assocVal];
}
} else {
$rows[] = $row;
}
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
fclose($fp);
}
if ($single && count($rows) == 1) {
return $rows[0];
}
if (count($rows) == 0) {
if ($single) {
return $type_map['false'];
}
return $type_map['true'];
}
return $rows;
}
/**
* Try to connect to the configured Database (during installation)
*
* @access public
* @param array input configuration array, holding the connection info
* @param array referenced array which holds the errors that might be encountered
* @return boolean return true on success, false on error
*/
public function probe($hash, &$errs)
{
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
if (!class_exists('SQLite3')) {
$errs[] = 'SQLite extension not installed. Available on PHP 5.4+.';
return false;
}
if (defined('S9Y_DATA_PATH')) {
// Shared installations!
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
} else {
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
}
$this->db_conn = new SQLite3($dbfile);
if ($this->db_conn) {
return true;
}
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
return false;
}
}

View File

@ -1,376 +0,0 @@
<?php
// Serendipity
// See LICENSE file for license information.
namespace Serendipity\Database;
use Serendipity\Database\DbAbstract;
class SqliteDatabase extends DbAbstract
{
/**
* Tells the DB Layer to start a DB transaction.
*
* @access public
*/
public function beginTransaction()
{
$this->query('begin transaction');
}
/**
* Tells the DB Layer to end a DB transaction.
*
* @access public
* @param boolean If true, perform the query. If false, rollback.
*/
public function endTransaction(bool $commit)
{
if ($commit) {
$this->query('commit transaction');
} else {
$this->query('rollback transaction');
}
}
/**
* Connect to the configured Database
*
* @access public
* @return resource connection handle
*/
public function connect()
{
if (isset($this->db_conn)) {
return $this->db_conn;
}
if (isset($this->serendipity['dbPersistent']) && $this->serendipity['dbPersistent']) {
$function = 'sqlite_popen';
} else {
$function = 'sqlite_open';
}
if (defined('S9Y_DATA_PATH')) {
$this->db_conn = $function(S9Y_DATA_PATH . $this->db_name . '.db');
} else {
$this->db_conn = $function($this->db_name . '.db');
}
return $this->db_conn;
}
/**
* Returns an escaped string, so that it can be safely included in a SQL string encapsulated within quotes, without allowing SQL injection.
*/
public function escapeString($string): string
{
static $search = array("\x00", '%', "'", '\"');
static $replace = array('%00', '%25', "''", '\\\"');
return str_replace($search, $replace, $string);
}
/**
* Returns the number of affected rows of a SQL query
*
* @access public
* @return int Number of affected rows
*/
public function affectedRows()
{
return sqlite_changes($this->db_conn);
}
/**
* Returns the number of updated rows in a SQL query
*
* @access public
* @return int Number of updated rows
*/
public function updatedRows()
{
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
return sqlite_changes($this->db_conn);
}
/**
* Returns the number of matched rows in a SQL query
*
* @access public
* @return int Number of matched rows
*/
public function matchedRows()
{
// It is unknown whether sqllite returns rows MATCHED or rows UPDATED
return sqlite_changes($this->db_conn);
}
/**
* Returns the latest INSERT_ID of an SQL INSERT INTO command, for auto-increment columns
*
* @access public
* @return int Value of the auto-increment column
*/
public function insertId()
{
return sqlite_last_insert_rowid($this->db_conn);
}
/**
* Parse result arrays into expected format for further operations
*
* SQLite does not support to return "e.entryid" within a $row['entryid'] return.
* So this function manually iteratse through all result rows and rewrites 'X.yyyy' to 'yyyy'.
* Yeah. This sucks. Don't tell me!
*
* @access private
* @param resource The row resource handle
* @param int Bitmask to tell whether to fetch numerical/associative arrays
* @return array Propper array containing the resource results
*/
protected function fetchArray($res, $type = SQLITE_BOTH)
{
static $search = array('%00', '%25');
static $replace = array("\x00", '%');
$row = sqlite_fetch_array($res, $type);
if (!is_array($row)) {
return $row;
}
/* strip any slashes, correct fieldname */
foreach ($row as $i => $v) {
// TODO: If a query of the format 'SELECT a.id, b.text FROM table' is used,
// the sqlite extension will give us key indizes 'a.id' and 'b.text'
// instead of just 'id' and 'text' like in mysql/postgresql extension.
// To fix that, we use a preg-regex; but that is quite performance costy.
// Either we always need to use 'SELECT a.id AS id, b.text AS text' in query,
// or the sqlite extension may get fixed. :-)
$row[preg_replace('@^.+\.(.*)@', '\1', $i)] = str_replace($search, $replace, $v);
}
return $row;
}
/**
* Assemble and return SQL condition for a "IN (...)" clause
*
* @access public
* @param string table column name
* @param array referenced array of values to search for in the "IN (...)" clause
* @param string condition of how to associate the different input values of the $search_ids parameter
* @return string resulting SQL string
*/
public function inSql($col, &$search_ids, $type = ' OR ')
{
$sql = array();
if (!is_array($search_ids)) {
return false;
}
foreach($search_ids AS $id) {
$sql[] = $col . ' = ' . $id;
}
$cond = '(' . implode($type, $sql) . ')';
return $cond;
}
/**
* Perform a DB Layer SQL query.
*
* This function returns values dependin on the input parameters and the result of the query.
* It can return:
* false or a string if there was an error (depends on $expectError),
* true if the query succeeded but did not generate any rows
* array of field values if it returned a single row and $single is true
* array of array of field values if it returned row(s) [stacked array]
*
* @access public
* @param string SQL query to execute
* @param boolean Toggle whether the expected result is a single row (TRUE) or multiple rows (FALSE). This affects whether the returned array is 1 or 2 dimensional!
* @param string Result type of the array indexing. Can be one of "assoc" (associative), "num" (numerical), "both" (numerical and associative, default)
* @param boolean If true, errors will be reported. If false, errors will be ignored.
* @param string A possible array key name, so that you can control the multi-dimensional mapping of an array by the key column
* @param string A possible array field name, so that you can control the multi-dimensional mapping of an array by the key column and the field value.
* @param boolean If true, the executed SQL error is known to fail, and should be disregarded (errors can be ignored on DUPLICATE INDEX queries and the likes)
* @return mixed Returns the result of the SQL query, depending on the input parameters
*/
public function &query($sql, $single = false, $result_type = "both", $reportErr = true, $assocKey = false, $assocVal = false, $expectError = false)
{
$type_map = array(
'assoc' => SQLITE_ASSOC,
'num' => SQLITE_NUM,
'both' => SQLITE_BOTH,
'true' => true,
'false' => false
);
static $debug = false;
if ($debug) {
// Open file and write directly. In case of crashes, the pointer needs to be killed.
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE QUERY: ' . $sql . "\n\n");
fclose($fp);
}
if ($reportErr && !$expectError) {
$res = sqlite_query($sql, $this->db_conn);
} else {
$res = @sqlite_query($sql, $this->db_conn);
}
if (!$res) {
if (!$expectError && !$this->serendipity['production']) {
var_dump($res);
var_dump($sql);
$msg = "problem with query";
return $msg;
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] [ERROR] ' . "\n\n");
fclose($fp);
}
return $type_map['false'];
}
if ($res === true) {
return $type_map['true'];
}
if (sqlite_num_rows($res) == 0) {
if ($single) {
return $type_map['false'];
}
return $type_map['true'];
} else {
$rows = array();
while (($row = $this->fetchArray($res, $type_map[$result_type]))) {
if (!empty($assocKey)) {
// You can fetch a key-associated array via the two function parameters assocKey and assocVal
if (empty($assocVal)) {
$rows[$row[$assocKey]] = $row;
} else {
$rows[$row[$assocKey]] = $row[$assocVal];
}
} else {
$rows[] = $row;
}
}
if ($debug) {
$fp = @fopen('sqlite.log', 'a');
fwrite($fp, '[' . date('d.m.Y H:i') . '] SQLITE RESULT: ' . print_r($rows, true). "\n\n");
fclose($fp);
}
if ($single && count($rows) == 1) {
return $rows[0];
}
return $rows;
}
}
/**
* Try to connect to the configured Database (during installation)
*
* @access public
* @param array input configuration array, holding the connection info
* @param array referenced array which holds the errors that might be encountered
* @return boolean return true on success, false on error
*/
public function probe($hash, &$errs)
{
$dbName = (isset($hash['sqlitedbName']) ? $hash['sqlitedbName'] : $hash['dbName']);
if (!function_exists('sqlite_open')) {
$errs[] = 'SQLite extension not installed. Run "pear install sqlite" on your webserver or contact your systems administrator regarding this problem.';
return false;
}
if (defined('S9Y_DATA_PATH')) {
// Shared installations!
$dbfile = S9Y_DATA_PATH . $dbName . '.db';
} else {
$dbfile = $this->serendipity['serendipityPath'] . $dbName . '.db';
}
$this->db_conn = sqlite_open($dbfile);
if ($this->db_conn) {
return true;
}
$errs[] = "Unable to open \"$dbfile\" - check permissions (directory needs to be writeable for webserver)!";
return false;
}
/**
* Prepares a Serendipity query input to fully valid SQL. Replaces certain "template" variables.
*
* @access public
* @param string SQL query with template variables to convert
* @return resource SQL resource handle of the executed query
*/
public function schemaImport($query)
{
static $search = array('{AUTOINCREMENT}', '{PRIMARY}', '{UNSIGNED}', '{FULLTEXT}', '{BOOLEAN}', '{UTF_8}', '{TEXT}');
static $replace = array('INTEGER', 'PRIMARY KEY', '', '', 'BOOLEAN NOT NULL', '', 'LONGTEXT');
if (stristr($query, '{FULLTEXT_MYSQL}')) {
return true;
}
$query = trim(str_replace($search, $replace, $query));
if ($query[0] == '@') {
// Errors are expected to happen (like duplicate index creation)
return $this->query(substr($query, 1), false, 'both', false, false, false, true);
} else {
return $this->query($query);
}
}
/**
* Returns the option to a LIMIT SQL statement, because it varies across DB systems
*
* @access public
* @param int Number of the first row to return data from
* @param int Number of rows to return
* @return string SQL string to pass to a LIMIT statement
*/
public function limit($start, $offset)
{
return $start . ', ' . $offset;
}
/**
* Return a LIMIT SQL option to the DB Layer as a full LIMIT statement
*
* @access public
* @param SQL string of a LIMIT option
* @return SQL string containing a full LIMIT statement
*/
public function limitSql($limitstring)
{
return ' LIMIT ' . $limitstring;
}
/**
* Returns the SQL code used for concatenating strings
*
* @access public
* @param string Input string/column to concatenate
* @return string SQL parameter
*/
public function concat($string)
{
return 'concat(' . $string . ')';
}
}

View File

@ -1,6 +1,6 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
require_once __DIR__ . '/../vendor/autoload.php';

View File

@ -1,11 +1,11 @@
<?php
// Serendipity
// 幸運な偶然 - Lucky Coinkydink
// See LICENSE file for license information.
require_once __DIR__ . '/lib/bootstrap.php';
use Serendipity\Routing;
use LuckyCoin\Routing;
define('IN_installer', true);
define('IN_upgrader', true);