Completed
Branch FET/variable-state-input-value... (cf176b)
by
unknown
75:01 queued 66:18
created

EEG_Aim::prepareStringForAuthnet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
use EventEspresso\core\services\formatters\AsciiOnly;
4
use EventEspresso\core\services\loaders\LoaderFactory;
5
6
/**
7
 * Event Espresso
8
 * Event Registration and Management Plugin for WordPress
9
 *
10
 * @package            Event Espresso
11
 * @author             Seth Shoultes
12
 * @copyright          (c) 2008-2011 Event Espresso  All Rights Reserved.
13
 * @license            http://eventespresso.com/support/terms-conditions/   * see Plugin Licensing *
14
 * @link               http://www.eventespresso.com
15
 * @version            4.3
16
 *
17
 * ------------------------------------------------------------------------
18
 *
19
 * EEG_Aim
20
 *
21
 * @package         Event Espresso
22
 * @subpackage
23
 * @author          Mike Nelson
24
 * ------------------------------------------------------------------------
25
 */
26
class EEG_Aim extends EE_Onsite_Gateway
27
{
28
29
    const LIVE_URL    = 'https://secure2.authorize.net/gateway/transact.dll'; // Authnet URL
30
31
    const SANDBOX_URL = 'https://test.authorize.net/gateway/transact.dll';
32
33
    protected $_login_id;
34
35
    protected $_transaction_key;
36
37
    protected $_server;
38
39
    protected $_currencies_supported = array(
40
        'AUD',
41
        'USD',
42
        'CAD',
43
        'EUR',
44
        'GBP',
45
        'NZD',
46
    );
47
48
    /**
49
     * Whether to send test transactions (even to live site)
50
     *
51
     * @var boolean
52
     */
53
    protected $_test_transactions;
54
55
    private $VERIFY_PEER = false;
56
57
    private $_x_post_fields = array(
58
        "version"        => "3.1",
59
        "delim_char"     => ",",
60
        "delim_data"     => "TRUE",
61
        "relay_response" => "FALSE",
62
        "encap_char"     => "|",
63
    );
64
65
    private $_additional_line_items = array();
66
67
    /**
68
     * A list of all fields in the AIM API.
69
     * Used to warn user if they try to set a field not offered in the API.
70
     */
71
    private $_all_aim_fields = array(
72
        "address",
73
        "allow_partial_auth",
74
        "amount",
75
        "auth_code",
76
        "authentication_indicator",
77
        "bank_aba_code",
78
        "bank_acct_name",
79
        "bank_acct_num",
80
        "bank_acct_type",
81
        "bank_check_number",
82
        "bank_name",
83
        "card_code",
84
        "card_num",
85
        "cardholder_authentication_value",
86
        "city",
87
        "company",
88
        "country",
89
        "cust_id",
90
        "customer_ip",
91
        "delim_char",
92
        "delim_data",
93
        "description",
94
        "duplicate_window",
95
        "duty",
96
        "echeck_type",
97
        "email",
98
        "email_customer",
99
        "encap_char",
100
        "exp_date",
101
        "fax",
102
        "first_name",
103
        "footer_email_receipt",
104
        "freight",
105
        "header_email_receipt",
106
        "invoice_num",
107
        "last_name",
108
        "line_item",
109
        "login",
110
        "method",
111
        "phone",
112
        "po_num",
113
        "recurring_billing",
114
        "relay_response",
115
        "ship_to_address",
116
        "ship_to_city",
117
        "ship_to_company",
118
        "ship_to_country",
119
        "ship_to_first_name",
120
        "ship_to_last_name",
121
        "ship_to_state",
122
        "ship_to_zip",
123
        "split_tender_id",
124
        "state",
125
        "tax",
126
        "tax_exempt",
127
        "test_request",
128
        "tran_key",
129
        "trans_id",
130
        "type",
131
        "version",
132
        "zip",
133
        "solution_id",
134
        "currency_code"
135
    );
136
137
138
    /**
139
     * Gets the URL where the request should go. This is filterable
140
     *
141
     * @return string
142
     */
143
    protected function _get_server_url()
144
    {
145
        return apply_filters(
146
            'FHEE__EEG_Aim___get_server_url',
147
            $this->_debug_mode ? self::SANDBOX_URL : self::LIVE_URL,
148
            $this
149
        );
150
    }
151
152
153
    /**
154
     * TEMPORARY CALLBACK! Do not use
155
     * Callback which filters the server url. This is added so site admins can revert to using
156
     * the old AIM server in case Akamai service breaks their integration.
157
     * Using Akamai will, however, be mandatory on June 30th 2016 Authorize.net
158
     * (see http://www.authorize.net/support/akamaifaqs/#firewall?utm_campaign=April%202016%20Technical%20Updates%20for%20Merchants.html&utm_medium=email&utm_source=Eloqua&elqTrackId=46103bdc375c411a979c2f658fc99074&elq=7026706360154fee9b6d588b27d8eb6a&elqaid=506&elqat=1&elqCampaignId=343)
159
     * Once that happens, this will be obsolete and WILL BE REMOVED.
160
     *
161
     * @param string $url
162
     * @param EEG_Aim $gateway_object
163
     * @return string
164
     */
165
    public function possibly_use_deprecated_aim_server($url, EEG_Aim $gateway_object)
166
    {
167
        if ($gateway_object->_server === 'authorize.net' && ! $gateway_object->_debug_mode) {
168
            return 'https://secure.authorize.net/gateway/transact.dll';
169
        } else {
170
            return $url;
171
        }
172
    }
173
174
175
    /**
176
     * Asks the gateway to do whatever it does to process the payment. Onsite gateways will
177
     * usually send a request directly to the payment provider and update the payment's status based on that;
178
     * whereas offsite gateways will usually just update the payment with the URL and query parameters to use
179
     * for sending the request via http_remote_request()
180
     *
181
     * @param EEI_Payment $payment
182
     * @param array $billing_info {
183
     *  @type $credit_card string
184
     *  @type $cvv string
185
     *  @type $exp_month string
186
     *  @type $exp_year string
187
     *  @see parent::do_direct_payment
188
     * }
189
     * @return EEI_Payment updated
190
     */
191
    public function do_direct_payment($payment, $billing_info = null)
192
    {
193
        add_filter('FHEE__EEG_Aim___get_server_url', array($this, 'possibly_use_deprecated_aim_server'), 10, 2);
194
        // Enable test mode if needed
195
        // 4007000000027  <-- test successful visa
196
        // 4222222222222  <-- test failure card number
197
198
        $item_num = 1;
199
        $transaction = $payment->transaction();
200
        $gateway_formatter = $this->_get_gateway_formatter();
201
        $order_description = $this->prepareStringForAuthnet($gateway_formatter->formatOrderDescription($payment));
202
        $primary_registrant = $transaction->primary_registration();
203
        // if we're are charging for the full amount, show the normal line items
204
        // and the itemized total adds up properly
205
        if ($this->_can_easily_itemize_transaction_for($payment)) {
206
            $total_line_item = $transaction->total_line_item();
207
            foreach ($total_line_item->get_items() as $line_item) {
208
                if ($line_item->quantity() == 0) {
209
                    continue;
210
                }
211
                $this->addLineItem(
212
                    $item_num++,
213
                    $gateway_formatter->formatLineItemName($line_item, $payment),
214
                    $gateway_formatter->formatLineItemDesc($line_item, $payment),
215
                    $line_item->quantity(),
216
                    $line_item->unit_price(),
217
                    'N'
218
                );
219
                $order_description .= $this->prepareStringForAuthnet($line_item->desc()) . ', ';
220
            }
221
            foreach ($total_line_item->tax_descendants() as $tax_line_item) {
222
                $this->addLineItem(
223
                    $item_num++,
224
                    $tax_line_item->name(),
225
                    $tax_line_item->desc(),
226
                    1,
227
                    $tax_line_item->total(),
228
                    'N'
229
                );
230
            }
231
        }
232
233
        // start transaction
234
        // if in debug mode, use authorize.net's sandbox id; otherwise use the Event Espresso partner id
235
        $partner_id = $this->_debug_mode ? 'AAA100302' : 'AAA105363';
236
        $this->setField('solution_id', $partner_id);
237
        $this->setField('amount', $gateway_formatter->formatCurrency($payment->amount()));
238
        $this->setField('description', substr(rtrim($order_description, ', '), 0, 255));
239
        $this->_set_sensitive_billing_data($billing_info);
240
        $this->setField('first_name', $billing_info['first_name']);
241
        $this->setField('last_name', $billing_info['last_name']);
242
        $this->setField('email', $billing_info['email']);
243
        $this->setField('company', $billing_info['company']);
244
        $this->setField('address', $billing_info['address'].' '.$billing_info['address2']);
245
        $this->setField('city', $billing_info['city']);
246
        $this->setField('state', $billing_info['state']);
247
        $this->setField('country', $billing_info['country']);
248
        $this->setField('zip', $billing_info['zip']);
249
        $this->setField('fax', $billing_info['fax']);
250
        $this->setField('cust_id', $primary_registrant->ID());
251
        $this->setField('phone', $billing_info['phone']);
252
        $currency_config = LoaderFactory::getLoader()->load('EE_Currency_Config');
253
        $this->setField('currency_code', $currency_config->code);
254
        // invoice_num would be nice to have it be unique per SPCO page-load, that way if users
255
        // press back, they don't submit a duplicate. However, we may be keeping the user on teh same spco page
256
        // in which case, we need to generate teh invoice num per request right here...
257
        $this->setField('invoice_num', wp_generate_password(12, false));// $billing_info['_reg-page-billing-invoice-'.$this->_gateway_name]['value']);
258
        // tell AIM that any duplicates sent in the next 5 minutes are to be ignored
259
        $this->setField('duplicate_window', 5 * MINUTE_IN_SECONDS);
260
261
        if ($this->_test_transactions) {
262
            $this->test_request = "true";
0 ignored issues
show
Bug introduced by
The property test_request does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
263
        }
264
265
        // Capture response
266
        $this->type = "AUTH_CAPTURE";
0 ignored issues
show
Bug introduced by
The property type does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
267
        $response = $this->_sendRequest($payment);
268
        if (! empty($response)) {
269
            if ($response->error_message) {
270
                $payment->set_status($this->_pay_model->failed_status());
271
                $payment->set_gateway_response($response->error_message);
272
            } else {
273
                $payment_status = $response->approved
274
                    ? $this->_pay_model->approved_status()
275
                    : $this->_pay_model->declined_status();
276
                $payment->set_status($payment_status);
277
                // make sure we interpret the AMT as a float, not an international string (where periods are thousand separators)
278
                $payment->set_amount((float) $response->amount);
279
                $payment->set_gateway_response(
280
                    sprintf(
281
                        esc_html__('%1$s (Reason Code: %2$s)', 'event_espresso'),
282
                        $response->response_reason_text,
283
                        $response->response_reason_code
284
                    )
285
                );
286
                if ($this->_debug_mode) {
287
                    $txn_id = $response->invoice_number;
288
                } else {
289
                    $txn_id = $response->transaction_id;
290
                }
291
                $payment->set_txn_id_chq_nmbr($txn_id);
292
            }
293
            $payment->set_extra_accntng($primary_registrant->reg_code());
294
            $payment->set_details(print_r($response, true));
295
        } else {
296
            $payment->set_status($this->_pay_model->failed_status());
297
            $payment->set_gateway_response(__("There was no response from Authorize.net", 'event_espresso'));
298
            $payment->set_details(print_r($response, true));
299
        }
300
        return $payment;
301
    }
302
303
304
    /**
305
     * Sets billing data for the upcoming request to AIM that is considered sensitive;
306
     * also this method can be overridden by children classes to easily change
307
     * what billing data gets sent
308
     *
309
     * @param array $billing_info
310
     */
311
    protected function _set_sensitive_billing_data($billing_info)
312
    {
313
        $this->setField('card_num', $billing_info['credit_card']);
314
        $this->setField('exp_date', $billing_info['exp_month'] . $billing_info['exp_year']);
315
        $this->setField('card_code', $billing_info['cvv']);
316
    }
317
318
319
    /**
320
     * Add a line item.
321
     *
322
     * @param string $item_id
323
     * @param string $item_name
324
     * @param string $item_description
325
     * @param string $item_quantity
326
     * @param string $item_unit_price
327
     * @param string $item_taxable
328
     */
329
    public function addLineItem($item_id, $item_name, $item_description, $item_quantity, $item_unit_price, $item_taxable)
330
    {
331
        $args = array(
332
            substr($item_id, 0, 31),
333
            substr($this->prepareStringForAuthnet($item_name), 0, 31),
334
            substr($this->prepareStringForAuthnet($item_description), 0, 255),
335
            number_format(abs($item_quantity), 2, '.', ''),
336
            number_format(abs($item_unit_price), 2, '.', ''),
337
            $item_taxable === 'N' ? 'N' : 'Y'
338
        );
339
        $this->_additional_line_items[] = implode('<|>', $args);
340
    }
341
342
343
    /**
344
     * Set an individual name/value pair. This will append x_ to the name
345
     * before posting.
346
     *
347
     * @param string $name
348
     * @param string $value
349
     * @throws AuthorizeNetException
350
     */
351
    protected function setField($name, $value)
352
    {
353
        if (in_array($name, $this->_all_aim_fields)) {
354
            $this->_x_post_fields[ $name ] = $value;
355
        } else {
356
            throw new AuthorizeNetException("Error: no field $name exists in the AIM API.
357
            To set a custom field use setCustomField('field','value') instead.");
358
        }
359
    }
360
361
362
    /**
363
     * Posts the request to AuthorizeNet & returns response.
364
     *
365
     * @param $payment
366
     * @return \EE_AuthorizeNetAIM_Response
367
     */
368
    private function _sendRequest($payment)
369
    {
370
        $this->_x_post_fields['login'] = $this->_login_id;
371
        $this->_x_post_fields['tran_key'] = $this->_transaction_key;
372
        $x_keys = array();
373 View Code Duplication
        foreach ($this->_x_post_fields as $key => $value) {
374
            $x_keys[] = "x_$key=" . urlencode($this->_get_unsupported_character_remover()->format($value));
375
        }
376
        // Add line items
377 View Code Duplication
        foreach ($this->_additional_line_items as $key => $value) {
378
            $x_keys[] =  "x_line_item=" . urlencode($this->_get_unsupported_character_remover()->format($value));
379
        }
380
        $this->_log_clean_request($x_keys, $payment);
381
        $post_url = $this->_get_server_url();
382
        $curl_request = curl_init($post_url);
383
        $post_body = implode("&", $x_keys);
384
        curl_setopt($curl_request, CURLOPT_POSTFIELDS, $post_body);
385
        curl_setopt($curl_request, CURLOPT_HEADER, 0);
386
        curl_setopt($curl_request, CURLOPT_TIMEOUT, 45);
387
        curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, 1);
388
        curl_setopt($curl_request, CURLOPT_SSL_VERIFYHOST, 2);
389
        if ($this->VERIFY_PEER) {
390
            curl_setopt($curl_request, CURLOPT_CAINFO, dirname(__DIR__) . '/ssl/cert.pem');
391
        } else {
392
            curl_setopt($curl_request, CURLOPT_SSL_VERIFYPEER, false);
393
        }
394
395
        if (preg_match('/xml/', $post_url)) {
396
            curl_setopt($curl_request, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
397
        }
398
399
        $response = curl_exec($curl_request);
400
401
        curl_close($curl_request);
402
        $response_obj =  new EE_AuthorizeNetAIM_Response($response);
403
404
        return $this->_log_and_clean_response($response_obj, $payment);
405
    }
406
407
408
    /**
409
     * Logs the clean data only
410
     *
411
     * @param array $request_array
412
     * @param EEI_Payment $payment
413
     */
414
    protected function _log_clean_request($request_array, $payment)
415
    {
416
        $keys_to_filter_out = array('x_card_num', 'x_card_code', 'x_exp_date');
417
        foreach ($request_array as $index => $keyvaltogether) {
418
            foreach ($keys_to_filter_out as $key) {
419
                if (strpos($keyvaltogether, $key) === 0) {
420
                    // found it at the first character
421
                    // so its one of them
422
                    unset($request_array[ $index ]);
423
                }
424
            }
425
        }
426
        $this->log(
427
            array(
428
                'AIM Request sent:' => $request_array,
429
                'Server URL'        => $this->_get_server_url()
430
            ),
431
            $payment
432
        );
433
    }
434
435
436
437
    /**
438
     * Logs the response and cleans it
439
     *
440
     * @param EE_AuthorizeNetAIM_Response $response_obj
441
     * @param EE_Payment                  $payment
442
     * @return \EE_AuthorizeNetAIM_Response
443
     */
444
    private function _log_and_clean_response($response_obj, $payment)
445
    {
446
        $response_obj->account_number = '';
447
        $this->log(array('AIM Response received:' => (array) $response_obj), $payment);
448
        return $response_obj;
449
    }
450
451
    /**
452
     * Removes characters Authorize.net doesn't handle well.
453
     * @since $VID:$
454
     * @param $text
455
     * @return string
456
     */
457
    private function prepareStringForAuthnet($text)
458
    {
459
        return str_replace(
460
            '\'',
461
            '',
462
            $text
463
        );
464
    }
465
}
466
467
468
469
/**
470
 * Class EE_AuthorizeNetAIM_Response
471
 *
472
 * Parses an AuthorizeNet AIM Response.
473
 *
474
 * @package    AuthorizeNet
475
 * @subpackage AuthorizeNetAIM
476
 */
477
class EE_AuthorizeNetAIM_Response
478
{
479
480
    const APPROVED = '1';
481
    const DECLINED = '2';
482
    const ERROR = '3';
483
    const HELD = '4';
484
485
    protected $_x_post_fields = array(
486
        "version"        => "3.1",
487
        "delim_char"     => ",",
488
        "delim_data"     => "TRUE",
489
        "relay_response" => "FALSE",
490
        "encap_char"     => "|",
491
    );
492
    public $approved;
493
    public $declined;
494
    public $error;
495
    public $held;
496
    public $response_code;
497
    public $response_subcode;
498
    public $response_reason_code;
499
    public $response_reason_text;
500
    public $authorization_code;
501
    public $avs_response;
502
    public $transaction_id;
503
    public $invoice_number;
504
    public $description;
505
    public $amount;
506
    public $method;
507
    public $transaction_type;
508
    public $customer_id;
509
    public $first_name;
510
    public $last_name;
511
    public $company;
512
    public $address;
513
    public $city;
514
    public $state;
515
    public $zip_code;
516
    public $country;
517
    public $phone;
518
    public $fax;
519
    public $email_address;
520
    public $ship_to_first_name;
521
    public $ship_to_last_name;
522
    public $ship_to_company;
523
    public $ship_to_address;
524
    public $ship_to_city;
525
    public $ship_to_state;
526
    public $ship_to_zip_code;
527
    public $ship_to_country;
528
    public $tax;
529
    public $duty;
530
    public $freight;
531
    public $tax_exempt;
532
    public $purchase_order_number;
533
    public $md5_hash;
534
    public $card_code_response;
535
    public $cavv_response; // cardholder_authentication_verification_response
536
    public $account_number;
537
    public $card_type;
538
    public $split_tender_id;
539
    public $requested_amount;
540
    public $balance_on_card;
541
    public $response; // The response string from AuthorizeNet.
542
    public $error_message;
543
    private $_response_array = array(); // An array with the split response.
544
545
546
    /**
547
     * Constructor. Parses the AuthorizeNet response string
548
     *
549
     * @param string $response The response from the AuthNet server.
550
     * @var string   $delimiter The delimiter used (default is ",")
551
     * @var string   $encap_char The encap_char used (default is "|")
552
     * @var array    $custom_fields Any custom fields set in the request.
553
     */
554
555
    public function __construct($response)
556
    {
557
        $encap_char = $this->_x_post_fields['encap_char'];
558
        $delimiter = $this->_x_post_fields['delim_char'];
559
        if ($response) {
560
            // Split Array
561
            $this->response = $response;
562
            if ($encap_char) {
563
                $this->_response_array = explode($encap_char . $delimiter . $encap_char, substr($response, 1, -1));
564
            } else {
565
                $this->_response_array = explode($delimiter, $response);
566
            }
567
568
            /**
569
             * If AuthorizeNet doesn't return a delimited response.
570
             */
571
            if (count($this->_response_array) < 10) {
572
                $this->approved = false;
573
                $this->error = true;
574
                $this->error_message = sprintf(
575
                    esc_html__('Unrecognized response from Authorize.net: %1$s', 'event_espresso'),
576
                    esc_html($response)
577
                );
578
                return;
579
            }
580
581
582
583
            // Set all fields
584
            $this->response_code = $this->_response_array[0];
585
            $this->response_subcode = $this->_response_array[1];
586
            $this->response_reason_code = $this->_response_array[2];
587
            $this->response_reason_text = $this->_response_array[3];
588
            $this->authorization_code = $this->_response_array[4];
589
            $this->avs_response = $this->_response_array[5];
590
            $this->transaction_id = $this->_response_array[6];
591
            $this->invoice_number = $this->_response_array[7];
592
            $this->description = $this->_response_array[8];
593
            $this->amount = $this->_response_array[9];
594
            $this->method = $this->_response_array[10];
595
            $this->transaction_type = $this->_response_array[11];
596
            $this->customer_id = $this->_response_array[12];
597
            $this->first_name = $this->_response_array[13];
598
            $this->last_name = $this->_response_array[14];
599
            $this->company = $this->_response_array[15];
600
            $this->address = $this->_response_array[16];
601
            $this->city = $this->_response_array[17];
602
            $this->state = $this->_response_array[18];
603
            $this->zip_code = $this->_response_array[19];
604
            $this->country = $this->_response_array[20];
605
            $this->phone = $this->_response_array[21];
606
            $this->fax = $this->_response_array[22];
607
            $this->email_address = $this->_response_array[23];
608
            $this->ship_to_first_name = $this->_response_array[24];
609
            $this->ship_to_last_name = $this->_response_array[25];
610
            $this->ship_to_company = $this->_response_array[26];
611
            $this->ship_to_address = $this->_response_array[27];
612
            $this->ship_to_city = $this->_response_array[28];
613
            $this->ship_to_state = $this->_response_array[29];
614
            $this->ship_to_zip_code = $this->_response_array[30];
615
            $this->ship_to_country = $this->_response_array[31];
616
            $this->tax = $this->_response_array[32];
617
            $this->duty = $this->_response_array[33];
618
            $this->freight = $this->_response_array[34];
619
            $this->tax_exempt = $this->_response_array[35];
620
            $this->purchase_order_number = $this->_response_array[36];
621
            $this->md5_hash = $this->_response_array[37];
622
            $this->card_code_response = $this->_response_array[38];
623
            $this->cavv_response = $this->_response_array[39];
624
            $this->account_number = $this->_response_array[50];
625
            $this->card_type = $this->_response_array[51];
626
            $this->split_tender_id = $this->_response_array[52];
627
            $this->requested_amount = $this->_response_array[53];
628
            $this->balance_on_card = $this->_response_array[54];
629
630
            $this->approved = ($this->response_code === self::APPROVED);
631
            $this->declined = ($this->response_code === self::DECLINED);
632
            $this->error = ($this->response_code === self::ERROR);
633
            $this->held = ($this->response_code === self::HELD);
634
        } else {
635
            $this->approved = false;
636
            $this->error = true;
637
            $this->error_message = esc_html__(
638
                'Error connecting to Authorize.net',
639
                'event_espresso'
640
            );
641
        }
642
    }
643
}
644
645
if (! class_exists('AuthorizeNetException')) {
646
    /**
647
     * Class AuthorizeNetException
648
     *
649
     * @package    AuthorizeNet
650
     */
651
    class AuthorizeNetException extends Exception
652
    {
653
654
        /**
655
         * Construct the exception. Note: The message is NOT binary safe.
656
         *
657
         * @link http://php.net/manual/en/exception.construct.php
658
         * @param string $message [optional] The Exception message to throw.
659
         * @param int $code [optional] The Exception code.
660
         * @param Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
661
         * @since 5.1.0
662
         */
663
        public function __construct($message = "", $code = 0, Exception $previous = null)
664
        {
665
            parent::__construct($message, $code, $previous);
666
        }
667
    }
668
}
669