: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
<?php namespace la\core\db;
use la\core\settings\LAGeneralSettings;
use la\core\settings\LASettingsUtils;
if ( ! defined( 'WPINC' ) ) die;
* @property LASafeMySQL $conn
* @author Looks Awesome <email@looks-awesome.com>
* @link https://looks-awesome.com
* @copyright Looks Awesome
abstract class LADBManager {
public $option_table_name;
public $posts_table_name;
public $cache_table_name;
public $streams_table_name;
public $image_cache_table_name;
public $streams_sources_table_name;
public $snapshot_table_name;
public $comments_table_name;
public $post_media_table_name;
protected $plugin_slug_down;
protected $sources = null;
protected $streams = null;
* LADBManager constructor.
function __construct($context) {
$this->context = $context;
$this->table_prefix = $context['table_name_prefix'];
$this->plugin_slug = LAUtils::slug($context);
$this->plugin_slug_down = LAUtils::slug_down($context);
$this->option_table_name = $this->table_prefix . 'options';
$this->posts_table_name = $this->table_prefix . 'posts';
$this->cache_table_name = $this->table_prefix . 'cache';
$this->streams_table_name = $this->table_prefix . 'streams';
$this->image_cache_table_name = $this->table_prefix . 'image_cache';
$this->streams_sources_table_name = $this->table_prefix . 'streams_sources';
$this->snapshot_table_name= $this->table_prefix . 'snapshots';
$this->comments_table_name= $this->table_prefix . 'comments';
$this->post_media_table_name = $this->table_prefix . 'post_media';
public function conn($reopen = false) {
if ($reopen || $this->conn == null){
$this->conn = LADB::create();
$this->conn->autocommit(true);
* @param false $only_enable
public final function dataInit($only_enable = false, $safe = false, $remote = true){
if ($safe && !LADDLUtils::existTable($conn, $this->streams_sources_table_name)) {
$sources = LADB::sources($conn, $this->cache_table_name, $this->streams_sources_table_name, null, $only_enable);
foreach ( $sources as $id => &$tmp_source ) {
if ($tmp_source['boosted'] == LASettingsUtils::YEP){
$boosted = $this->getBoostSources();
if (isset($boosted[$id])){
$tmp_source = $boosted[$id];
$this->sources = $sources;
$this->streams = LADB::streams($conn, $this->streams_table_name);
$connections = $conn->getIndMultiRow('stream_id', 'select `stream_id`, `feed_id` from ?n order by `stream_id`', $this->streams_sources_table_name);
foreach ( $this->streams as &$stream ) {
$stream = (array)LADB::unserializeStream($stream);
if (!isset($stream['feeds'])) $stream['feeds'] = [];
if (isset($connections[$stream['id']])){
foreach ($connections[$stream['id']] as $source){
if (isset($this->sources[$source['feed_id']])){
$full_source = $this->sources[$source['feed_id']];
$stream['feeds'][] = $full_source;
if (isset($full_source['status']) && $full_source['status'] == 0) $stream['status'] = '0';
* Get stream settings by id endpoint
public final function get_stream_settings(){
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$id = $_GET['stream-id'];
$this->dataInit(false, false);
$stream = $this->streams[$id];
// cleaning if error was saved in database stream model, can be removed in future, now it's needed for affected users
if ( isset( $stream['error'] ) ) unset( $stream['error'] );
die( json_encode( $stream ) );
public final function get_shortcode_pages() {
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$stream = $_POST['stream'];
$query = "SELECT ID, post_title FROM " . $wpdb->posts . " WHERE post_content LIKE '%s' AND post_status = 'publish'";
$results = $wpdb->get_results ( $wpdb->prepare( $query, '%[ff id="' . $stream . '"%' ) );
foreach ($results as $result) {
$result->url = get_permalink( $result->ID );
die( json_encode( $results ) );
public final function create_stream(){
$stream = $this->getStreamFromRequestWithoutErrors();
$conn->beginTransaction();
if (false !== ($max = LADB::maxIdOfStreams( $conn, $this->streams_table_name))){
$newId = (string) ($max + 1);
$stream->feeds = isset($stream->feeds) ? $stream->feeds : json_encode( [] );
$stream->name = isset($stream->name) ? $stream->name : '';
LADB::setStream($conn, $this->streams_table_name, $this->streams_sources_table_name, $newId, $stream);
$response = json_encode(LADB::getStream($conn, $this->streams_table_name, $newId));
$this->refreshCache($newId);
$conn->rollbackAndClose();
echo 'Caught exception: ' . $e->getMessage() . "\n";
public final function save_sources_settings(){
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
if (isset($_POST['model'])){
$model = $_POST['model'];
$model['id'] = 1; // DON'T DELETE, ID is always 1, this is needed to detect if model was saved
if (isset( $dontChange ) && isset( $_POST['model']['feeds_changed'] )) {
unset( $_POST['model']['feeds_changed'] );
if (isset($_POST['model']['feeds_changed'])){
foreach ( $_POST['model']['feeds_changed'] as $feed ) {
switch ($feed['state']) {
$source = $_POST['model']['feeds'][ $feed['id'] ];
$original_status = $source['status'];
$sources = LADB::sources($this->conn(), $this->cache_table_name, $this->streams_sources_table_name);
$old = $sources[$source['id']];
$changed_content = $this->changedContent($source, $old);
$this->cleanFeed($feed['id']);
$boosted = LASettingsUtils::YepNope2ClassicStyle($source['boosted'], false);
if (!$boosted && ($source['boosted'] != $old['boosted'])){
$boosted = LASettingsUtils::YepNope2ClassicStyle($source['boosted'], false);
$this->modifySource( $source, $changed_content );
if ($source['enabled'] == 'yep') {
$this->refreshCache4Source($feed['id'], false, $boosted);
$source = $_POST['model']['feeds'][$feed['id']];
$this->modifySource($source);
$boosted = LASettingsUtils::YepNope2ClassicStyle($source['boosted'], false);
$this->refreshCache4Source($feed['id'], true, $boosted);
$source = $_POST['model']['feeds'][ $feed['id'] ];
$this->cleanFeed($feed['id']);
$boosted = LASettingsUtils::YepNope2ClassicStyle($source['boosted'], false);
$this->refreshCache4Source($feed['id'], true, $boosted);
$sources = LADB::sources($this->conn(), $this->cache_table_name, $this->streams_sources_table_name);
if (isset($sources[$feed['id']])){
$source = $sources[$feed['id']];
$this->deleteFeed($feed['id']);
$boosted = LASettingsUtils::YepNope2ClassicStyle($source['boosted'], false);
$response = $this->proxyRequest($_POST);
if ($response->code == 200){
foreach ( $json['feeds'] as &$feed ) {
$enabled = LASettingsUtils::YepNope2ClassicStyle($feed['enabled'], false) ? 1 : 0;
$status = ['last_update' => $feed['last_update'], 'status' => $original_status, 'enabled' => $enabled];
if (isset($feed['errors']) && is_array($feed['errors'])) $status['errors'] = serialize($feed['errors']);
$this->saveSource($feed['id'], $status);
$feed['last_update'] = $feed['last_update'] == 0 ? 'N/A' : LASettingsUtils::classicStyleDate($feed['last_update']);
else if ($response->code == 504){
header('HTTP/1.1 504 Gateway Time-out');
else if ($response->code == 403){
header('HTTP/1.1 403 Forbidden');
echo $response->raw_body;
if (isset($model['feeds'])){
$sources = $this->sources();
foreach ( $model['feeds'] as &$source ) {
if (array_key_exists($source['id'], $sources)){
$source = $sources[$source['id']];
if (isset( $dontChange )) {
$model['error'] = 'Not allowed';
echo json_encode($model);
public final function save_stream_settings(){
$stream = $this->getStreamFromRequestWithoutErrors();
$conn->beginTransaction();
$stream->last_changes = time();
LADB::setStream($conn, $this->streams_table_name, $this->streams_sources_table_name, $stream->id, $stream);
$this->generateCss($stream);
echo json_encode($stream);
$this->proxyRequest($_POST);
$conn->rollbackAndClose();
error_log('save_stream_settings error:');
error_log($e->getMessage());
error_log($e->getTraceAsString());
* Save general settings endpoint
public final function ff_save_settings_fn() {
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$serialized_settings = $_POST['settings']; // param1=foo¶m2=bar
parse_str( $serialized_settings, $settings );
$activated = $this->activate($settings);
$force_load_cache = $this->clean_cache($settings);
$conn->beginTransaction();
$settings = $this->saveGeneralSettings($settings);
$_POST['wp_locale'] = json_encode($wp_locale);
$_POST['wp_timezone_string'] = get_option( 'timezone_string' );
$_POST['wp_date_format'] = get_option( 'date_format' );
$_POST['wp_time_format'] = get_option( 'time_format' );
if (false !== ($option = get_option( 'la_facebook_access_token', false )))
$_POST['la_facebook_access_token'] = $option;
if (false !== ($option = get_option( 'la_facebook_access_token_expires', false )))
$_POST['la_facebook_access_token_expires'] = $option;
$this->proxyRequest($_POST);
$this->refreshCache(null, $force_load_cache);
'activated' => $activated
$this->customizeResponse($response);
echo json_encode( $response );
error_log('ff_save_settings_fn error:');
if ( strpos( $msg, 'Connection timed out after') !== false ) {
$msg .= '. Failed to connect to https://flow.looks-awesome.com which validates purchase code. Please ask help from your hosting support and tell them curl_exec exits with connection timeout error on line 889 of wp-content/plugins/flow-flow/includes/db/LADBManager.php';
error_log($e->getTraceAsString());
$conn->rollbackAndClose();
* @param mixed $old_value
* @noinspection PhpUnusedParameterInspection
public function update_wp_date_format_hook($old_value, $value, $option){
if ($option === 'WPLANG'){
switch_to_locale(empty($value) ? 'en_US' : $value);
$post['action'] = 'flow_flow_save_settings_date_format';
$post['wp_locale'] = json_encode($wp_locale);
$post['wp_timezone_string'] = get_option( 'timezone_string' );
$post['wp_date_format'] = get_option( 'date_format' );
$post['wp_time_format'] = get_option( 'time_format' );
$this->proxyRequest($post);
public function update_options(){
'action' => 'flow_flow_save_settings',
'options' => $this->getOption('options', false, false, true),
'fb_auth_options' => $this->getOption('fb_auth_options', false, false, true)
if (false != ($la_facebook_access_token = get_option('la_facebook_access_token', false))){
$data['la_facebook_access_token'] = $la_facebook_access_token;
if (false != ($la_facebook_access_token_expires = get_option('la_facebook_access_token_expires', false))) {
$data['la_facebook_access_token_expires'] = $la_facebook_access_token_expires;
$this->proxyRequest($data);
public function get_sources( ) {
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$this->dataInit(false, false);
if (isset($_REQUEST['id']) && !empty($_REQUEST['id'])){
if (isset($this->sources[$_REQUEST['id']])){
die( json_encode( $this->sources[$_REQUEST['id']] ) );
header("HTTP/1.0 404 Not Found");
die( json_encode( $this->sources ) );
public final function get_boosts(){
if ( !current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
if ( isset($_POST['not_active']) ) {
'status' => 'never_used', // 'active', 'cancelled', 'paused'
echo json_encode( $response );
if (null != ($token = $this->getToken())){
$response = Request::post(FF_BOOST_SERVER . 'flow-flow/ff', [
'Content-Type: application/x-www-form-urlencoded'