How to get parse errors with DOMDocument::loadXML() - php

I am trying to load xml which has mismatched tags and I expected something like this to work but without luck.
try{
$xml=new \DOMDocument('1.0','utf-8');
$xml->loadXML(file_get_contents($file),
}catch (\Exception $e){
echo $e->getMessage());
}
Now I really need to throw an exception for parse errors. I tried to pass options to loadXML
LIBXML_ERR_ERROR|LIBXML_ERR_FATAL|LIBXML_ERR_WARNING
again no luck. Please guide me how to catch all these parse errors.
Edit
As suggested by #Ghost in comments, I came around this solution
abstract class XmlReadStrategy extends AbstractReadStrategy
{
/** #var array */
protected $importAttributes;
/**
* #param $fileFullPath
* #param $fileName
*/
public function __construct($fileFullPath,$fileName)
{
parent::__construct($fileFullPath,$fileName);
libxml_use_internal_errors(true);
}
/**
*
*/
protected function handleXmlException(){
$this->dataSrc=array();
foreach(libxml_get_errors() as $e){
$this->logger->append(Logger::ERROR,'[Error] '.$e->message);
}
}
/**
* Import xml file
* #param string $file
* #throws \Exception
*/
protected function loadImportFileData($file)
{
try{
$xml=new \DOMDocument('1.0','utf-8');
if(!$xml->loadXML(file_get_contents($file))){
$this->handleXmlException();
}
$this->dataSrc=$this->nodeFilter($xml);
}catch (\Exception $e){
$this->logger->append(Logger::ERROR,$e->getMessage());
$this->dataSrc=array();
}
}
....
}
So the trick is to call libxml_use_internal_errors(true); and then check loadXML() status e.g
if(!$xml->loadXML(file_get_contents($file))){
$this->handleXmlException();
}
I don't know if this libxml_use_internal_errors(true); has any side-effect so far

You can enable libxml_use_internal_errors and get errors with libxml_get_errors()
libxml_use_internal_errors(true);
$xml = new DOMDocument('1.0','utf-8');
if ( !$xml->loadxml(file_get_contents($file)) ) {
$errors = libxml_get_errors();
var_dump($errors);
}

Related

How to print custom message in try catch - php laravel

I need to write to log some data when exception occurred, since it is dynamic data and I need the message I tried with simple code, just to write 'Boom' if exception occurred.
However just the automatic exception data written in logs (as before the try catch).
can someone advise how to print to log in the catch?
I just need to add additional text to the exception, just to be more specific
try {
$sheet->setCellValue($cell,$innervalue);
} catch(Exception $e) {
$message = 'Can not set value: '.$innervalue .' in cell ' .$cell .$headerArray[$headerIndex];
\Log::info('boom');
}
and in the logs nothing displayed
php artisan make:Exception SomeAweSomeException
This way you have your own controle of how the exception should look and handle. Also you can use standards wich makes you code look cleaner.
class SomeCustomException extends Exception
{
// ...
/**
* Get the exception's context information.
*
* #return array
*/
public function context()
{
return ['order_id' => $this->orderId];
}
}
use Exception;
class SetDeliverDateToFarAwayException extends Exception
{
public function __construct()
{
parent::__construct(
'Selecting more than three days into the future is not allowed'
);
}
}
and now my method wich creates the error message.
/**
* Finalizes the scheduler
*
* #return string
* #throws SetDeliverDateToFarAwayException|Throwable
*/
public function set(): string
{
$today = Carbon::now();
$deliver = new Carbon();
$deliver->year($this->year ?? $today->year);
$deliver->month($this->month ?? $today->month);
$deliver->day($this->day ?? $today->day);
$deliver->hour($this->hour ?? $today->hour);
$deliver->minute($this->minute ?? $today->minute);
#Mail gun can max schedule mail for 3 days in the future. So we throw an Exception if the date is to big.
throw_if($deliver->diffInHours($today) > 72, new SetDeliverDateToFarAwayException());
return Mail::schedule($deliver->toDateTimeString());
}
this what worked for me
try {
$sheet->setCellValueExplicit('WELL DONE');
}
catch (\Exception $e) {
$errorMsg = 'the value of:\' ' .$innervalue . '\' in cell ' . $cell . ' in header: ' . $headerArray[$headerIndex] . 'problematic' ;
\Log::error($errorMsg);
\Log::error($e->getMessage());
\Log::error($e->getTraceAsString());
throw new Exception($e->getMessage());
}
To get the exception log message use it like this -
try {
} catch(Exception $e) {
Log::info(json_encode($e->getMessage()));
}

How to unit test this try catch

I'm trying to 100% code coverage my service. Here is a method:
<?php
* Search to public accounts.
*
* #param string $query
*
* #return TwitterResponse
*/
public function search(string $query): TwitterResponse
{
try {
$response = $this->client->getClient()->get(UserEnum::URI_SEARCH, [
'query' => ['q' => $query,]
]);
} catch (ClientException $e) {
$response = $e->getResponse();
}
return new TwitterResponse($response);
}
It simply GET a user with Twitter API.
In my opinion, I should develop two tests : one for the try and one for the catch. Bellow is my test for the try.
<?php
/**
* #return void
*/
public function setUp(): void
{
$this->prophet = new Prophet();
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willReturn(new TwitterResponse(new Response()));
$this->client->post(Argument::any(), Argument::any())->willReturn(new TwitterResponse(new Response()));
$this->twitterClient = $this->prophet->prophesize(TwitterClient::class);
$this->twitterClient->getClient()->willReturn($this->client);
$this->userService = new UserService($this->twitterClient->reveal());
}
/**
* Tests if a TwitterResponse is returned with status HTTP_OK.
*
* #return void
*/
public function testGetOk(): void
{
$actual = $this->userService->get('');
$this->assertEquals(get_class($actual), TwitterResponse::class);
$this->assertEquals(HttpResponse::HTTP_OK, $actual->getStatusCode());
}
Bellow the code coverage of get().
As you can see, I don't test the catch case. How can I do it ? I already tried to mock a 404 HTTP response catch something but it did not work. Do you have an idea of how I can do it ?
Thanks.
EDIT : I tried this for the catch case ->
public function testGetKo(): void
{
$response = new TwitterResponse(new Response(HttpResponse::HTTP_NOT_FOUND));
$response->setStatusCode(HttpResponse::HTTP_NOT_FOUND);
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willReturn($response);
$this->twitterClient = $this->prophet->prophesize(TwitterClient::class);
$actual = $this->userService->get('');
$this->assertEquals(get_class($actual), TwitterResponse::class);
$this->assertEquals(HttpResponse::HTTP_NOT_FOUND, $actual->getStatusCode());
}
Phpunit returns : Failed asserting that 200 matches expected 404. It seems that my mock client doesn't work well.
I know, it's an old post, but..
Maybe try to mock client, and when it's triggered throw exception?
So when you throw ClientException, you should check for TwitterResponse result. And when you throw DummyException, you should expect DummyException.
This is untested, as I don't usually use prophecy, but I do simialry with other mocking frameworks:
public function testGetKo(): void
{
// ... other setup
$exception = new ClientException();
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willThrow($exception);
And you will likely add $this->expectException(ClientException::class); before you run the function being tested.

Issue with accessing property in Google_Service_Exception

I'm following a tutorial on how to use the Google Reseller API. I've come to the section on determining whether a customer already exists in Google Apps (Step 2) but have come unstuck in handling the Google_Service_Exception object.
If a customer doesn't exist then a call to the API will return a 404 error. I'm using the code property of the Google_Service_Exception object $e to determine if the response has a 404 error. However when I try to return the error code with $e->code with:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->code == 404){
return false;
}
}
I get the following PHP error:
Fatal error: Cannot access protected property Google_Service_Exception::$code.
The Google_Service_Exception class is as follows:
<?php
require_once 'Google/Exception.php';
class Google_Service_Exception extends Google_Exception
{
/**
* Optional list of errors returned in a JSON body of an HTTP error response.
*/
protected $errors = array();
/**
* Override default constructor to add ability to set $errors.
*
* #param string $message
* #param int $code
* #param Exception|null $previous
* #param [{string, string}] errors List of errors returned in an HTTP
* response. Defaults to [].
*/
public function __construct(
$message,
$code = 0,
Exception $previous = null,
$errors = array()
) {
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
parent::__construct($message, $code, $previous);
} else {
parent::__construct($message, $code);
}
$this->errors = $errors;
}
/**
* An example of the possible errors returned.
*
* {
* "domain": "global",
* "reason": "authError",
* "message": "Invalid Credentials",
* "locationType": "header",
* "location": "Authorization",
* }
*
* #return [{string, string}] List of errors return in an HTTP response or [].
*/
public function getErrors()
{
return $this->errors;
}
}
So I assumed the error has something to do with the fact that $errors is protected. I imagine it is protected for a reason so I was a bit wary of changing the class. Any help / pointers in working around this error would be greatly appreciated. Thanks
Just use the getCode() method:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->getCode() == 404){ // <- Change is here
return false;
}
}
Google_Service_Exception extends Google_Exception and Google_Exception extends Exception. You may read the documentation about Exception here. You will see the getCode method.

`BulkWrite error :: norepl` mongodb php BulkWrite

I getting this is exception in my application, and don't understand why. This exception throw in for one collection, over collection not throw this exception, with processed insert(),update(), or delete()
/**
* insert to mongo db method
* #param $dataArray
* #param $collection
*/
static function insert($dataArray, $collection) {
$connect = Core_Model_Mongo::getConnect();
$write = new MongoDB\Driver\BulkWrite();
$writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY);
$write->insert($dataArray);
$connect->executeBulkWrite(
Config_Db::getConf()['mongodb']['db'].'.'.$collection,
$write,
$writeConcern
);
}
end get connect method
/**
* #return \MongoDB\Driver\Manager
*/
static function getConnect() {
if(!is_null(self::$_connect)) {
return self::$_connect;
}
self::$_connect = new \MongoDB\Driver\Manager(Config_Db::getConf()['mongodb']['connect']);
return self::$_connect;
}
data save in collection success full, and this exception not take insert(),update() and other methods. I temporary resolve this question by try catch block
try {
// my code
} catch(MongoDB\Driver\Exception\BulkWriteException $error) {
Core_App::log(var_export($error,true));
}
but this is resolve not correct because I need application without any exception,error,notice, and over log level error.

Should I check if array/collection value exists before trying to get it?

Should I check if a key exists and then get it or just get it (when I need to get it, not check if its set)?
What is more reliable? safer? faster?
Examples:
1) PHP redis (https://github.com/nicolasff/phpredis)
if ($redis->exists('key'))
echo $redis->get('key');
// VS
if ($value = $redis->get('key'))
echo $value;
2) PHP phalcon cookies(http://docs.phalconphp.com/pt/latest/reference/cookies.html)
if ($this->cookies->has('remember-me'))
echo $this->cookies->get('remember-me')->getValue()
// VS
if ($value = $this->cookies->get('remember-me')->getValue())
echo $value;
Thank you!
My interpretation of this problem is:
I don't like writing things like
if ($value = $redis->get('key'))
echo $value;
it makes code unclear.
Also, why is checking if variable exists so important? Because it simplifies control flow.
Let's consider that you're grabbing some data from service to render it on page. You can write low quality code with multiple ifs, but you can also try something like this:
offerServiceImpl.php
class offerServiceImpl implements offerService {
//... (some methods)
/**
* #param int $offerId
* #return Offer
* #throws InvalidArgumentException
* #throws RuntimeException
*/
public function getOffer($offerId)
{
if (!$offerId || !is_numeric($offerId)) {
throw new InvalidArgumentException("Invalid offer id: " . $offerId);
}
$offer = $this->offerDao->get($offerId);
if (!$offer) {
//could be your own exception class
throw new RuntimeException("Could not found offer " . $offerId);
} else {
return $offer;
}
}
}
offersControler.php
class offersController extends AbstractController{
public function index($id){
//... some code
try{
$offer = $this->offerService->getOffer($id);
} catch (InvalidArgumentException $ex) {
//log error, perform redirect to error 500
} catch (RuntimeException $ex){
//log another error, perform redirect to error 404
} catch (Exception $ex){
//log error, perform redirect to error 500
}
}
}

Categories