首页 > 解决方案 > PHP injects bogus unreadable characters into array after it crosses a certain size

问题描述

This issue has been causing me a headache over the last month. What this code does is take a list of client sites and measures it against a list of contractor addresses. Each client site is a key to an array with 5 objects (5 nearest contractors) as its values. The problem happens when I give it more than ~30 client addresses to match it against my database (205 vendors) and find the closest 5 vendors for each site, PHP injects bogus characters(as pictured). Also, when these characters show up(they show up towards the end of the output), the array never finishes as it stops before the closing bracket or a few lines before that, this happens both before I use json_encode and after.

The bogus characters change around but they're usually : �</p>

What I've tried:

  1. Encoding everything to UTF-8 (This includes all values in the code as well as in the file settings themselves)
  2. Change my database from running off a CSV table to a MySQL table.
  3. Used JsonSerializable for my objects.
  4. Raising the memory limit in my php.ini.
  5. Fiddling with the json_encode options

What I know:

  1. The bogus characters are entered into the array itself, not after using json_encode().
  2. Results are the same whether I encode to UTF-8 or not.
  3. This didn't show up when I used json_encode for each client site separately (but was stuck not knowing how to append the json arrays into one large json array after the loop is done)

parseclientsite.php

<?php


$tmpName ="clientsitelist.xlsx";


require "vendor/autoload.php";
include("Libraries/SimpleXLSX.php");
include("database.php");


$xlsx = SimpleXLSX::parse($tmpName);
$xlsx = $xlsx->rows();
parseSiteFile($xlsx);


function parseSiteFile($siteArray){

    $nomenclatureBuilding = ["Street Address", "Site Address", "Address", "Site", "street address", "Street address"];
    $nomenclaturePostcode = ["Postcode", "Post code", "post code", "postcode", "Post Code"];

    $n = 0;
    $b = false;
    $correctHeaders = $correctPairing = [];
    $correctList = array();
    
    foreach ($siteArray as $key => $value) {
        if (!$b) {
            //first row header
            for ($i = 0; $i <= count($value) - 1; $i++) {
                $correctHeaders[$value[$i]] = "";
                if (in_array($value[$i], $nomenclatureBuilding,)) {
                    $streetColumn = trim($value[$i]);
                }
                if (in_array($value[$i], $nomenclaturePostcode,)) {
                    $postcodeColumn = trim($value[$i]);
                }
            }
            $b = true;
            continue;
        }
        foreach ($correctHeaders as $key2 => $value2) {
            $correctPairing[$key2] = trim($value[$n]) ?? null;

            $n++;
        }
        $n = 0;
        if (isset($postcodeColumn)) {
            $siteAddress = trim($correctPairing[$streetColumn] . " " . $correctPairing[$postcodeColumn] . ", Australia");
            array_push($correctList, createSingleQuery($siteAddress));
        } else {
            $siteAddress = trim($correctPairing[$streetColumn] . ", Australia");
            array_push($correctList, createSingleQuery($siteAddress));
        }
    }
    echo json_encode($correctList,  JSON_PARTIAL_OUTPUT_ON_ERROR| JSON_PRETTY_PRINT | JSON_UNESCAPED_LINE_TERMINATORS| JSON_INVALID_UTF8_IGNORE);
}

function createSingleQuery($siteAddress)
{
    $httpClient = new Client();
    $provider = new GoogleMaps($httpClient, null, 'key');
    $geocoder = new StatefulGeocoder($provider, 'en');
    $siteCoordinates = $geocoder->geocodeQuery(GeocodeQuery::create($siteAddress));
    if ($siteCoordinates->isEmpty() == 1) {
        $latitude1 = "0";
        $longitude1 = "0";
    } else {
        $latitude1 = trim($siteCoordinates->get(0)->getCoordinates()->getLatitude());
        $longitude1 = trim($siteCoordinates->get(0)->getCoordinates()->getLongitude());
    }
    $correctList[$siteAddress] = readDatabase($latitude1, $longitude1);
    return $correctList;
}

?>

database.php

include("classes.php");

if (isset($_GET['location'])) {
    $query = $_GET['location'];
    singleQuery($query);
}



function readDatabase($latitude1, $longitude1){
    $dbhost = 'localhost';
    $dbuser = 'user';
    $dbpass = 'password';
    $conn = mysqli_connect($dbhost, $dbuser, $dbpass, 'vendors', 0,'/var/run/mysqld/mysqld.sock');
    
    $sql = "SELECT BHS_Rate_Ex_GST, After_Hours_Rate_Ex_GST, Phone_Number, Company_Name, Street_Address, Charges_KM_Rate, Latitude, Longitude  FROM mytable";
    $result = $conn->query($sql);
    $measurement = array();
    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {

            $vendorSite = array();
            array_push($vendorSite, $row["Latitude"], $row["Longitude"]);

            array_push($measurement, new Vendor(
                $row["BHS_Rate_Ex_GST"],$row["After_Hours_Rate_Ex_GST"], $row["Phone_Number"], $row["Company_Name"], $row["Street_Address"], compareDistance($latitude1, $longitude1, $vendorSite), $row["Charges_KM_Rate"]));
        }
        usort($measurement,function($first,$second){
            return $first->Distance <=> $second->Distance;
        });
        return $measurement = array_slice($measurement,0, 5);
    }

    $conn->close();
}


function compareDistance($latitude1, $longitude1, $vendorSite)
{
    $earthMeanRadius = 6371;
    $latitude2 = floatval($vendorSite[0]);
    $longitude2 = floatval($vendorSite[1]);
    $dLat = deg2rad($latitude2 - $latitude1);
    $dLon = deg2rad($longitude2 - $longitude1);
    $a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon / 2) * sin($dLon / 2);
    $c = 2 * asin(sqrt($a));
    return round(($earthMeanRadius * $c), 2);
}

classes.php

class Vendor {
    public function __construct($Cost, $Ah, $Phone, $Name, $Address, $Distance, $Travel) {
        $this->Cost = $Cost;
        $this->Ah = $Ah;
        $this->Phone = $Phone;
        $this->Name = $Name;
        $this->Address = $Address;
        $this->Distance = $Distance;
        $this->Travel = $Travel;
    }
    public function __toString() {
        return $this->Cost . ' ' . $this->Ah . ' ' .$this->Phone . ' ' . $this->Name
            . ' ' . $this->Address . ' ' . $this->Distance. ' ' . $this->Travel;
    }
}

标签: phpunicode

解决方案


代码没问题,PHPstorm 的服务器是罪魁祸首。


推荐阅读