Trying to use FPDI with TCPDF using Symfony2 - php

I'm trying to use FPDI with TCPDF in symfony2.
I could use TCPDF alone without any problems. But when I try to use FPDI, I'm getting errors.
In my composer.json i put the following:
"autoload": {
"psr-0": { "": "src/"
},
"classmap": ["vendor/tcpdf/tcpdf.php","vendor/fpdi/fpdi.php"]
},
Then in the controller I can instantiate a TCPDF class
$tcpdf = new \TCPDF();
But when I try to instantiate a FPDI instance
$fpdi = new \FPDI();
Symfony throws me the following error when I try to access the route.
*FatalErrorException: Error: Class 'Symfony\Component\Debug\Exception\ContextErrorException' not found in C:\xampp\htdocs\Consulta\vendor\fpdi\fpdi2tcpdf_bridge.php line 169
in C:\xampp\htdocs\Consulta\vendor\fpdi\fpdi2tcpdf_bridge.php line 169*
Does anyone know how to solve this ?
I'm adding the content of fpdi2tcpdf_bridge.php
<?php
//
// FPDI - Version 1.4.4
//
// Copyright 2004-2013 Setasign - Jan Slabon
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/**
* This class is used as a bridge between TCPDF and FPDI
* and will create the possibility to use both FPDF and TCPDF
* via one FPDI version.
*
* We'll simply remap TCPDF to FPDF again.
*
* It'll be loaded and extended by FPDF_TPL.
*/
class FPDF extends TCPDF {
function _putstream($s) {
$this->_out($this->_getstream($s));
}
function _getxobjectdict() {
$out = parent::_getxobjectdict();
if (count($this->tpls)) {
foreach($this->tpls as $tplidx => $tpl) {
$out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
}
}
return $out;
}
/**
* Encryption of imported data by FPDI
*
* #param array $value
*/
function pdf_write_value(&$value) {
switch ($value[0]) {
case PDF_TYPE_STRING:
if ($this->encrypted) {
$value[1] = $this->_unescape($value[1]);
$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
$value[1] = TCPDF_STATIC::_escape($value[1]);
}
break;
case PDF_TYPE_STREAM:
if ($this->encrypted) {
$value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
$value[1][1]['/Length'] = array(
PDF_TYPE_NUMERIC,
strlen($value[2][1])
);
}
break;
case PDF_TYPE_HEX:
if ($this->encrypted) {
$value[1] = $this->hex2str($value[1]);
$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
// remake hexstring of encrypted string
$value[1] = $this->str2hex($value[1]);
}
break;
}
}
/**
* Unescapes a PDF string
*
* #param string $s
* #return string
*/
function _unescape($s) {
$out = '';
for ($count = 0, $n = strlen($s); $count < $n; $count++) {
if ($s[$count] != '\\' || $count == $n-1) {
$out .= $s[$count];
} else {
switch ($s[++$count]) {
case ')':
case '(':
case '\\':
$out .= $s[$count];
break;
case 'f':
$out .= chr(0x0C);
break;
case 'b':
$out .= chr(0x08);
break;
case 't':
$out .= chr(0x09);
break;
case 'r':
$out .= chr(0x0D);
break;
case 'n':
$out .= chr(0x0A);
break;
case "\r":
if ($count != $n-1 && $s[$count+1] == "\n")
$count++;
break;
case "\n":
break;
default:
// Octal-Values
if (ord($s[$count]) >= ord('0') &&
ord($s[$count]) <= ord('9')) {
$oct = ''. $s[$count];
if (ord($s[$count+1]) >= ord('0') &&
ord($s[$count+1]) <= ord('9')) {
$oct .= $s[++$count];
if (ord($s[$count+1]) >= ord('0') &&
ord($s[$count+1]) <= ord('9')) {
$oct .= $s[++$count];
}
}
$out .= chr(octdec($oct));
} else {
$out .= $s[$count];
}
}
}
}
return $out;
}
/**
* Hexadecimal to string
*
* #param string $hex
* #return string
*/
function hex2str($hex) {
return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
}
/**
* String to hexadecimal
*
* #param string $str
* #return string
*/
function str2hex($str) {
return current(unpack('H*', $str));
}
}

Add the packagist reference to composer.json the normal way:
https://packagist.org/packages/setasign/fpdi
"setasign/fpdi": "1.4.2"
That appears to be an alias of https://github.com/mark9000/FPDI.git
That should autoload it correctly.

Related

How to create model, tables, etc. WITHOUT command line interface

Just started to work with Propel 2.0 ORM. All tutorials are telling to work with schemas this way:
Create schema in XML/JSON/YAML/PHP file;
Run $ propel model:build
How do I create, or re-create, or update models and data without using the command line but just inside the php scripts? It might be necessary for creating CMS module installers or something like this.
The Answer: Commander Class
A «Reinvent-The-Wheel» approach but I did not found any other way to work with Propel 2 without CLI.
use Propel\Runtime\Propel;
use Propel\Generator\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Application;
/**
* Class Commander
*
* A script-based approach to run Propel commands without the CLI.
*
* Usage:
*
* ```
* $cmd = new Commander('<propel_command>', '<command_arguments>');
* $cmd->run();
* ...
* $cmd->addCommand('<propel_command>', '<command_arguments>');
* $cmd->run();
* ```
*
* In case of migration tasks you must call
* ```
* ...->preMigrate(array('<path_to_schema_files_dir1>', ..., '<path_to_schema_files_dirN>'), '<temp_dir>');
* ```
* to gather all schemas together and analyze 'em with propel:diff.
*
* Then after the diff and migrate are complete you must call ``postMigrate()`` to remove temporary
* schema copies.
*
*/
class Commander
{
private $command,
$parameters,
$migrationTempSource;
public function __construct($cmd = '', $params = '')
{
$this->addCommand($cmd, $params);
}
/**
* Prepare schema files to be analyzed before the migration process.
* #param array $schemaDirs Array of strings with schema directories
* #param string $tmpSchemaDir Temporary directory to copy schemas to.
* This path also must be used as a --schema-dir option value during the
* diff and migrate tasks
* #return boolean $result
*/
public function preMigrate($schemaDirs = array(), $tmpSchemaDir)
{
$result = false;
$filelist = [];
foreach($schemaDirs as $path)
{
if(is_dir($path))
{
$f = $this->seekFiles($path);
$filelist = count($f) > 0 ? array_merge($filelist, $f) : $f;
}
}
if(!file_exists($tmpSchemaDir))
{
mkdir($tmpSchemaDir, 0777, true);
}
foreach($schemaDirs as $path)
{
if(is_dir($path))
{
$f = $this->seekFiles($path);
foreach($f as $file)
{
copy($path . '/' . $file, $tmpSchemaDir . '/' . $file);
}
}
}
$this->migrationTempSource = $tmpSchemaDir;
return $result;
}
/**
* Removes the temporary schema files after the diff and migrate tasks are complete.
*
* #param bool $removeTmpDir Set to true if you want to remove the whole temporary
* directory, not just the schema files.
* #return bool
*/
public function postMigrate($removeTmpDir = false)
{
$result = false;
$dir = scandir($this->migrationTempSource);
foreach($dir as $d)
{
if($d != '.' && $d != '..')
{
unlink($this->migrationTempSource . '/' . $d);
}
}
if($removeTmpDir === true)
{
#rmdir($this->migrationTempSource);
}
return $result;
}
private function seekFiles($dir)
{
$res = [];
if(is_dir($dir))
{
$d = scandir($dir);
foreach($d as $dd)
{
if($dd != '.' && $dd != '..')
{
if((strpos($dd, 'schema.xml') == strlen($dd)-10) || ($dd == 'schema.xml'))
{
$res[] = $dd;
}
}
}
}
return $res;
}
public function addCommand($cmd = '', $params = '')
{
$this->command = $cmd;
$this->parameters = explode(' --', $params);
}
public function run()
{
if($this->command == '') return false;
$callCommandClass = '';
$cmdParts = explode(':', $this->command);
switch($cmdParts[0])
{
case 'config':
switch($cmdParts[1])
{
case 'convert':
$callCommandClass = 'ConfigConvertCommand';
break;
}
break;
case 'diff':
$callCommandClass = 'MigrationDiffCommand';
break;
case 'migration':
switch($cmdParts[1])
{
case 'create':
$callCommandClass = 'MigrationCreateCommand';
break;
case 'diff':
$callCommandClass = 'MigrationDiffCommand';
break;
case 'up':
$callCommandClass = 'MigrationUpCommand';
break;
case 'down':
$callCommandClass = 'MigrationDownCommand';
break;
case 'status':
$callCommandClass = 'MigrationStatusCommand';
break;
case 'migrate':
$callCommandClass = 'MigrationMigrateCommand';
break;
}
break;
case 'model':
switch($cmdParts[1])
{
case 'build':
$callCommandClass = 'ModelBuildCommand';
break;
}
break;
case 'sql':
switch($cmdParts[1])
{
case 'build':
$callCommandClass = 'SqlBuildCommand';
break;
case 'insert':
$callCommandClass = 'SqlInsertCommand';
break;
}
break;
}
$a = [];
foreach($this->parameters as $p)
{
$x = explode('=', $p);
if(count($x) > 1)
{
$a['--'.str_replace('--', '', $x[0])] = trim($x[1]);
}
else
{
$a['--'.str_replace('--', '', $x[0])] = true;
}
}
$commandLine = array('command' => $this->command) + $a;
$app = new Application('Propel', Propel::VERSION);
$cls = '\Propel\Generator\Command'.'\\'.$callCommandClass;
/** #noinspection PhpParamsInspection */
$app->add(new $cls());
$app->setAutoExit(false);
$output = new StreamOutput(fopen("php://temp", 'r+'));
$result = $app->run(new ArrayInput($commandLine), $output);
if(0 !== $result)
{
rewind($output->getStream());
return stream_get_contents($output->getStream());
}
else
{
return true;
}
}
}
And the usage example:
//Convert the configuration file
$cmd = new Commander('config:convert', '--config-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/config --output-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/config');
$cmd->run();
//... or (re)build models
$cmd = new Commander('model:build', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/module/schema --output-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/module/models');
$cmd->run();
//... or perform database migration (actually not tested yet :/ )
$cmd = new Commander('migration:diff', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache');
$cmd->preMigrate([$_SERVER['DOCUMENT_ROOT'].'/propeltest/schema', $_SERVER['DOCUMENT_ROOT'].'/propeltest/module/schema'], $_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache');
$cmd->run(); // runs migrate:diff
$cmd->addCommand('migration:diff', '--schema-dir='.$_SERVER['DOCUMENT_ROOT'].'/propeltest/cache/schemacache'); // prepare to actually migration
$cmd->run(); // perform migration:migrate
$cmd->postMigrate();

Magento cron script "Call to undefined method"

I'm getting a PHP fatal error on a cron script used for Commission Junction. It seems in the error_log it keeps updating with
PHP Fatal error: Call to undefined method Mage_Core_Helper_Data::getEscapedCSVData() in /home/lovescen/public_html/app/code/core/Mage/Dataflow/Model/Convert/Parser/Csv.php on line 269
The code on line 269 is
$escapedValue = Mage::helper("core")->getEscapedCSVData(array($value));
And here is the entire code in Csv.php.
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license#magento.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magento.com for more information.
*
* #category Mage
* #package Mage_Dataflow
* #copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
* #license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* Convert csv parser
*
* #category Mage
* #package Mage_Dataflow
* #author Magento Core Team <core#magentocommerce.com>
*/
class Mage_Dataflow_Model_Convert_Parser_Csv extends Mage_Dataflow_Model_Convert_Parser_Abstract
{
protected $_fields;
protected $_mapfields = array();
public function parse()
{
// fixed for multibyte characters
setlocale(LC_ALL, Mage::app()->getLocale()->getLocaleCode().'.UTF-8');
$fDel = $this->getVar('delimiter', ',');
$fEnc = $this->getVar('enclose', '"');
if ($fDel == '\t') {
$fDel = "\t";
}
$adapterName = $this->getVar('adapter', null);
$adapterMethod = $this->getVar('method', 'saveRow');
if (!$adapterName || !$adapterMethod) {
$message = Mage::helper('dataflow')->__('Please declare "adapter" and "method" nodes first.');
$this->addException($message, Mage_Dataflow_Model_Convert_Exception::FATAL);
return $this;
}
try {
$adapter = Mage::getModel($adapterName);
}
catch (Exception $e) {
$message = Mage::helper('dataflow')->__('Declared adapter %s was not found.', $adapterName);
$this->addException($message, Mage_Dataflow_Model_Convert_Exception::FATAL);
return $this;
}
if (!method_exists($adapter, $adapterMethod)) {
$message = Mage::helper('dataflow')->__('Method "%s" not defined in adapter %s.', $adapterMethod, $adapterName);
$this->addException($message, Mage_Dataflow_Model_Convert_Exception::FATAL);
return $this;
}
$batchModel = $this->getBatchModel();
$batchIoAdapter = $this->getBatchModel()->getIoAdapter();
if (Mage::app()->getRequest()->getParam('files')) {
$file = Mage::app()->getConfig()->getTempVarDir().'/import/'
. urldecode(Mage::app()->getRequest()->getParam('files'));
$this->_copy($file);
}
$batchIoAdapter->open(false);
$isFieldNames = $this->getVar('fieldnames', '') == 'true' ? true : false;
if (!$isFieldNames && is_array($this->getVar('map'))) {
$fieldNames = $this->getVar('map');
}
else {
$fieldNames = array();
foreach ($batchIoAdapter->read(true, $fDel, $fEnc) as $v) {
$fieldNames[$v] = $v;
}
}
$countRows = 0;
while (($csvData = $batchIoAdapter->read(true, $fDel, $fEnc)) !== false) {
if (count($csvData) == 1 && $csvData[0] === null) {
continue;
}
$itemData = array();
$countRows ++; $i = 0;
foreach ($fieldNames as $field) {
$itemData[$field] = isset($csvData[$i]) ? $csvData[$i] : null;
$i ++;
}
$batchImportModel = $this->getBatchImportModel()
->setId(null)
->setBatchId($this->getBatchModel()->getId())
->setBatchData($itemData)
->setStatus(1)
->save();
}
$this->addException(Mage::helper('dataflow')->__('Found %d rows.', $countRows));
$this->addException(Mage::helper('dataflow')->__('Starting %s :: %s', $adapterName, $adapterMethod));
$batchModel->setParams($this->getVars())
->setAdapter($adapterName)
->save();
//$adapter->$adapterMethod();
return $this;
// // fix for field mapping
// if ($mapfields = $this->getProfile()->getDataflowProfile()) {
// $this->_mapfields = array_values($mapfields['gui_data']['map'][$mapfields['entity_type']]['db']);
// } // end
//
// if (!$this->getVar('fieldnames') && !$this->_mapfields) {
// $this->addException('Please define field mapping', Mage_Dataflow_Model_Convert_Exception::FATAL);
// return;
// }
//
// if ($this->getVar('adapter') && $this->getVar('method')) {
// $adapter = Mage::getModel($this->getVar('adapter'));
// }
//
// $i = 0;
// while (($line = fgetcsv($fh, null, $fDel, $fEnc)) !== FALSE) {
// $row = $this->parseRow($i, $line);
//
// if (!$this->getVar('fieldnames') && $i == 0 && $row) {
// $i = 1;
// }
//
// if ($row) {
// $loadMethod = $this->getVar('method');
// $adapter->$loadMethod(compact('i', 'row'));
// }
// $i++;
// }
//
// return $this;
}
public function parseRow($i, $line)
{
if (sizeof($line) == 1) return false;
if (0==$i) {
if ($this->getVar('fieldnames')) {
$this->_fields = $line;
return;
} else {
foreach ($line as $j=>$f) {
$this->_fields[$j] = $this->_mapfields[$j];
}
}
}
$resultRow = array();
foreach ($this->_fields as $j=>$f) {
$resultRow[$f] = isset($line[$j]) ? $line[$j] : '';
}
return $resultRow;
}
/**
* Read data collection and write to temporary file
*
* #return Mage_Dataflow_Model_Convert_Parser_Csv
*/
public function unparse()
{
$batchExport = $this->getBatchExportModel()
->setBatchId($this->getBatchModel()->getId());
$fieldList = $this->getBatchModel()->getFieldList();
$batchExportIds = $batchExport->getIdCollection();
$io = $this->getBatchModel()->getIoAdapter();
$io->open();
if (!$batchExportIds) {
$io->write("");
$io->close();
return $this;
}
if ($this->getVar('fieldnames')) {
$csvData = $this->getCsvString($fieldList);
$io->write($csvData);
}
foreach ($batchExportIds as $batchExportId) {
$csvData = array();
$batchExport->load($batchExportId);
$row = $batchExport->getBatchData();
foreach ($fieldList as $field) {
$csvData[] = isset($row[$field]) ? $row[$field] : '';
}
$csvData = $this->getCsvString($csvData);
$io->write($csvData);
}
$io->close();
return $this;
}
public function unparseRow($args)
{
$i = $args['i'];
$row = $args['row'];
$fDel = $this->getVar('delimiter', ',');
$fEnc = $this->getVar('enclose', '"');
$fEsc = $this->getVar('escape', '\\');
$lDel = "\r\n";
if ($fDel == '\t') {
$fDel = "\t";
}
$line = array();
foreach ($this->_fields as $f) {
$v = isset($row[$f]) ? str_replace(array('"', '\\'), array($fEnc.'"', $fEsc.'\\'), $row[$f]) : '';
$line[] = $fEnc.$v.$fEnc;
}
return join($fDel, $line);
}
/**
* Retrieve csv string from array
*
* #param array $fields
* #return string
*/
public function getCsvString($fields = array()) {
$delimiter = $this->getVar('delimiter', ',');
$enclosure = $this->getVar('enclose', '');
$escapeChar = $this->getVar('escape', '\\');
if ($delimiter == '\t') {
$delimiter = "\t";
}
$str = '';
foreach ($fields as $value) {
$escapedValue = Mage::helper("core")->getEscapedCSVData(array($value));
$value = $escapedValue[0];
if (strpos($value, $delimiter) !== false ||
empty($enclosure) ||
strpos($value, $enclosure) !== false ||
strpos($value, "\n") !== false ||
strpos($value, "\r") !== false ||
strpos($value, "\t") !== false ||
strpos($value, ' ') !== false) {
$str2 = $enclosure;
$escaped = 0;
$len = strlen($value);
for ($i=0;$i<$len;$i++) {
if ($value[$i] == $escapeChar) {
$escaped = 1;
} else if (!$escaped && $value[$i] == $enclosure) {
$str2 .= $enclosure;
} else {
$escaped = 0;
}
$str2 .= $value[$i];
}
$str2 .= $enclosure;
$str .= $str2.$delimiter;
} else {
$str .= $enclosure.$value.$enclosure.$delimiter;
}
}
return substr($str, 0, -1) . "\n";
}
}
Not sure how to fix this problem. If I could get some help I'd really appreciate it.
Thanks
UPDATE
Here is the code from my app/code/core/Mage/Core/Helper/Data.php file: http://pastie.org/10815259
Does the file app/code/core/Mage/Core/Helper/Data.php have the getEscapedCSVData function in it?
It appears that this function is added by SUPEE-7405. Have you patched your store with this SUPEE?
In my case some previous programer have copy the file
from core
\app\code\core\Mage\Core\Helper\Data.php
to local
app\code\local\Mage\Core\Helper\Data.php
and then apply SUPE PATCH 7405...
it cause that all modifications made by SUPE in data.php where rewrite by the old ones in local folder...
Solution: copy the new data.php to local or made a extension of class (better solution)

Sagepay 2.22 to 3.00 form upgrade - PHP update to AES encryption

I've been trying with no avail to understand how to upgrade the encryption used in the code for my company's three ecommerce sites from simpleXor to an AES encryption. Without doing this, I cannot upgrade the rest of the code which means after July we won't be able to take any payments online.
I've managed to update the crypt form codes in line with the upgrade to 3.00.
I can identify the encryption code and have looked through the form integration demo download for PHP from Sagepay but can't find anything that looks remotely similar to my encryption code?!
Could somebody point me in the right direction for finding a suitable encryption code to replace the old one with!?
Our sites are based on JShop and I have one file for sending and a response file.
This is the file for sending info:
<?php function startProcessor($orderNumber) { global $dbA,$orderArray,$jssStoreWebDirHTTP,$jssStoreWebDirHTTPS,$cartMain; $callBack = "$jssStoreWebDirHTTPS"."gateways/response/protx.php"; $cDetails = returnCurrencyDetails($orderArray["currencyID"]); $gatewayOptions = retrieveGatewayOptions("PROTX"); switch ($gatewayOptions["testMode"]) { case "S": $myAction = "https://test.sagepay.com/Simulator/VSPFormGateway.asp"; break; case "Y": $myAction = "https://test.sagepay.com/gateway/service/vspform-register.vsp"; break; case "N": $myAction = "https://live.sagepay.com/gateway/service/vspform-register.vsp"; break; } $myVendor = $gatewayOptions["vendor"]; $myEncryptionPassword = $gatewayOptions["encryptionPassword"]; $billingAddress = $orderArray["address1"]."\n"; if ($orderArray["address2"] != "") { $billingAddress .= $orderArray["address2"]."\n"; } $billingAddress .= $orderArray["town"]."\n"; $billingAddress .= $orderArray["county"]."\n"; $billingAddress .= $orderArray["country"]; $deliveryAddress = $orderArray["deliveryAddress1"]."\n"; if ($orderArray["deliveryAddress2"] != "") { $deliveryAddress .= $orderArray["deliveryAddress2"]."\n"; } $deliveryAddress .= $orderArray["deliveryTown"]."\n"; $deliveryAddress .= $orderArray["deliveryCounty"]."\n"; $deliveryAddress .= $orderArray["deliveryCountry"]; $crypt = "VendorTxCode=$orderNumber";
$crypt .= "&Amount=".number_format($orderArray["orderTotal"],$cDetails["decimals"],'.','');
$crypt .= "&Currency=".#$cDetails["code"];
$crypt .= "&Description=".$gatewayOptions["description"];
$crypt .= "&SuccessURL=$callBack?xOid=$orderNumber&xRn=".$orderArray["randID"];
$crypt .= "&FailureURL=$callBack?xOid=$orderNumber&xRn=".$orderArray["randID"];
$crypt .= "&BillingSurname=".$orderArray["surname"];
$crypt .= "&BillingFirstnames=".$orderArray["forename"];
$crypt .= "&BillingAddress1=".$orderArray["address1"];
$crypt .= "&BillingCity=".$orderArray["town"];
$crypt .= "&BillingPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["postcode"]);
$crypt .= "&BillingCountry=".$orderArray["country"];
$crypt .= "&DeliverySurname=".&orderArray["surname"];
$crypt .= "&DeliveryFirstnames=".&orderArray["forename"];
if ($orderArray["deliveryPostcode"] != "") {
$crypt .= "&DeliveryAddress1=".$orderArray["deliveryAddress1"];
$crypt .= "&DeliveryCity=".$orderArray["deliveryTown"];
$crypt .= "&DeliveryPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["deliveryPostcode"]);
$crypt .= "&DeliveryCountry=".$orderArray["deliveryCountry"]; }
else {
$crypt .= "&DeliveryAddress1=".$orderArray["address1"];
$crypt .= "&DeliveryCity=".$orderArray["town"];
$crypt .= "&DeliveryPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["postcode"]);
$crypt .= "&DeliveryCountry=".$orderArray["country"]; }
$crypt .= "&BillingPhone=".preg_replace("/[^\sa-zA-Z0-9]/", "", $orderArray["telephone"]);
if ($gatewayOptions["sendEmail"] == 1) {
$crypt .= "&CustomerEmail=".$orderArray["email"]; }
$crypt .= "&VendorEmail=".$gatewayOptions["vendorEmail"];
$crypt .= "&ApplyAVSCV2=".$gatewayOptions["cvvCheck"];
$crypt .= "&Apply3DSecure=".$gatewayOptions["3DSecure"];
$crypt = base64_encode(protx_simpleXor($crypt,$myEncryptionPassword));
$tpl = createTSysObject(templatesCreatePath($cartMain["templateSet"]),"gatewaytransfer.html",$requiredVars,0);
$gArray["method"] = "POST";
$gArray["action"] = $myAction;
$gArray["fields"][] = array("name"=>"VPSProtocol","value"=>"3.00");
$gArray["fields"][] = array("name"=>"Vendor","value"=>$myVendor);
$gArray["fields"][] = array("name"=>"TxType","value"=>$gatewayOptions["txType"]); $gArray["fields"][] = array("name"=>"Crypt","value"=>$crypt);
$mArray = $gArray;
$gArray["process"] = "document.automaticForm.submit();";
$tpl->addVariable("shop",templateVarsShopRetrieve());
$tpl->addVariable("labels",templateVarsLabelsRetrieve());
$tpl->addVariable("automaticForm",$gArray);
$tpl->addVariable("manualForm",$mArray);
$tpl->showPage(); }
function protx_simpleXor($inString, $key) { $outString=""; $l=0; if (strlen($inString)!=0) { for ($i = 0; $i < strlen($inString); $i++) { $outString=$outString . ($inString[$i]^$key[$l]); $l++; if ($l==strlen($key)) { $l=0; } } } return $outString; } ?>
This is the response file:
<?php
/*================ JShop Server ================
= (c)2003-2010 Whorl Ltd. =
= All Rights Reserved =
= Redistribution of this file is prohibited. =
= http://www.jshop.co.uk/ =
==============================================*/
?><?php
define("IN_JSHOP", TRUE);
include("../../static/config.php");
include("../../routines/dbAccess_".$databaseType.".php");
include("../../routines/tSys.php");
include("../../routines/general.php");
include("../../routines/stockControl.php");
include("../../routines/emailOutput.php");
dbConnect($dbA);
$orderID = makeSafe(getFORM("xOid"));
$newOrderID = $orderID;
$randID = makeSafe(getFORM("xRn"));
$crypt = makeSafe(getFORM("crypt"));
$gatewayOptions = retrieveGatewayOptions("PROTX");
$orderID = makeInteger($orderID) - retrieveOption("orderNumberOffset");
$result = $dbA->query("select * from $tableOrdersHeaders where orderID=$orderID and randID='$randID'");
if ($dbA->count($result) == 0 || $crypt=="") {
doRedirect_JavaScript($jssStoreWebDirHTTP."index.php");
exit;
}
$orderArray = $dbA->fetch($result);
$ccResult = $dbA->query("select * from $tablePaymentOptions where paymentID=".$orderArray["paymentID"]);
$poRecord = $dbA->fetch($ccResult);
$paidStatus = $poRecord["statusID"];
$crypt = str_replace(" ","+",$crypt);
$crypt = protx_simpleXor(base64_decode($crypt),$gatewayOptions["encryptionPassword"]);
$nameValues = explode("&",$crypt);
$resultCode = "";
for ($f = 0; $f < count($nameValues); $f++) {
$thisCode = explode("=",$nameValues[$f]);
$resultCode[$thisCode[0]] = $thisCode[1];
}
if ($resultCode["VendorTxCode"] != $newOrderID) {
doRedirect_JavaScript($jssStoreWebDirHTTP."index.php");
exit;
}
$authResponse = "&Status Result=".$resultCode["Status"]."&AVS/CV2 Check=".#$resultCode["AVSCV2"]."&Address Result=".#$resultCode["AddressResult"]."&Postcode Result=".#$resultCode["PostCodeResult"]."&CV2 Result=".#$resultCode["CV2Result"]."&3d Secure Status=".#$resultCode["3DSecureStatus"];
$randID = $orderArray["randID"];
if ($orderArray["status"] != $paidStatus) {
$dt=date("YmdHis",createOffsetTime());
switch ($resultCode["Status"]) {
case "OK":
case "AUTHENTICATED":
case "REGISTERED":
$authResponse="Gateway=Sage Pay&Authorisation Code=".$resultCode["TxAuthNo"]."&Sage Pay Transaction ID=".$resultCode["VPSTxId"]."&Status=Payment Confirmed".$authResponse;
$dbA->query("update $tableOrdersHeaders set status=$paidStatus, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");
$orderArray["status"] = $paidStatus;
//ok, this is where we should do the stock control then.
include("process/paidProcessList.php");
doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
break;
case "REJECTED":
$authResponse="Gateway=Sage Pay&Status=Payment Rejected Due To Rules".$authResponse;
$dbA->query("update $tableOrdersHeaders set status=3, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");
include("process/failProcessList.php");
doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
break;
default:
if ($orderArray["status"] == 1) {
$authResponse="Gateway=Sage Pay&Status=Payment Failed".$authResponse;
$dbA->query("update $tableOrdersHeaders set status=3, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");
include("process/failProcessList.php");
}
doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
break;
}
} else {
doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
}
function protx_simpleXor($inString, $key) {
$outString="";
$l=0;
if (strlen($inString)!=0) {
for ($i = 0; $i < strlen($inString); $i++) {
$outString=$outString . ($inString[$i]^$key[$l]);
$l++;
if ($l==strlen($key)) { $l=0; }
}
}
return $outString;
}
?>
It looks like you are using very old code. I would recommend you try to completely port/rewrite your code using the official Sage Pay integration libraries to avoid strange bugs with things like escaping.
If you're in a rush, here is a stripped down version of the class you need to get the encryption/decryption part working.
<?php
class SagepayUtil
{
/**
* PHP's mcrypt does not have built in PKCS5 Padding, so we use this.
*
* #param string $input The input string.
*
* #return string The string with padding.
*/
static protected function addPKCS5Padding($input)
{
$blockSize = 16;
$padd = "";
// Pad input to an even block size boundary.
$length = $blockSize - (strlen($input) % $blockSize);
for ($i = 1; $i <= $length; $i++)
{
$padd .= chr($length);
}
return $input . $padd;
}
/**
* Remove PKCS5 Padding from a string.
*
* #param string $input The decrypted string.
*
* #return string String without the padding.
* #throws SagepayApiException
*/
static protected function removePKCS5Padding($input)
{
$blockSize = 16;
$padChar = ord($input[strlen($input) - 1]);
/* Check for PadChar is less then Block size */
if ($padChar > $blockSize)
{
throw new SagepayApiException('Invalid encryption string');
}
/* Check by padding by character mask */
if (strspn($input, chr($padChar), strlen($input) - $padChar) != $padChar)
{
throw new SagepayApiException('Invalid encryption string');
}
$unpadded = substr($input, 0, (-1) * $padChar);
/* Chech result for printable characters */
if (preg_match('/[[:^print:]]/', $unpadded))
{
throw new SagepayApiException('Invalid encryption string');
}
return $unpadded;
}
/**
* Encrypt a string ready to send to SagePay using encryption key.
*
* #param string $string The unencrypyted string.
* #param string $key The encryption key.
*
* #return string The encrypted string.
*/
static public function encryptAes($string, $key)
{
// AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
// Add PKCS5 padding to the text to be encypted.
$string = self::addPKCS5Padding($string);
// Perform encryption with PHP's MCRYPT module.
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);
// Perform hex encoding and return.
return "#" . strtoupper(bin2hex($crypt));
}
/**
* Decode a returned string from SagePay.
*
* #param string $strIn The encrypted String.
* #param string $password The encyption password used to encrypt the string.
*
* #return string The unecrypted string.
* #throws SagepayApiException
*/
static public function decryptAes($strIn, $password)
{
// HEX decoding then AES decryption, CBC blocking with PKCS5 padding.
// Use initialization vector (IV) set from $str_encryption_password.
$strInitVector = $password;
// Remove the first char which is # to flag this is AES encrypted and HEX decoding.
$hex = substr($strIn, 1);
// Throw exception if string is malformed
if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
{
throw new SagepayApiException('Invalid encryption string');
}
$strIn = pack('H*', $hex);
// Perform decryption with PHP's MCRYPT module.
$string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector);
return self::removePKCS5Padding($string);
}
}
Setup your variables as you do now.
$gatewayOptions["encryptionPassword"] = "PASSWORDPASSWORD";
$crypt = "Your=Sagepay&Query=String";
Then for encryption (sending to sagepay) use this
//$crypt = base64_encode(protx_simpleXor($crypt,$myEncryptionPassword));
$crypt = SagepayUtil::encryptAes($crypt, $gatewayOptions["encryptionPassword"]);
var_dump($crypt);
//#714DCF7FE82FADA4B9F8CF53982D56FFC8F2021A5FC10481409ED1FD7BF6880E
And for decryption (receiving from sagepay) use this
//$crypt = protx_simpleXor(base64_decode($crypt),$gatewayOptions["encryptionPassword"]);
$crypt = SagepayUtil::decryptAes($crypt, $gatewayOptions["encryptionPassword"]);
var_dump($crypt);
//Your=Sagepay&Query=String
Edit: Removed the base64encoding layer. Protx will assume a base64 encoded string is using the old XOR method so you must not use base64 encoding. Instead it is necessary to use hex encoding and start the string with the "#" character.

Can't find what I should change from "Undefined offset: 1" PHP notice

I've got this error :
PHP Notice: Undefined offset: 1 in /var/www/joomla/Joomla_3/modules/mod_menu/helper.php on line 59
I found after searching that it might be due to the first parameter of an array that is not defined. But I tried many things and I can't find what I should change to make this work without the notice.
Here is the helper.php :
<?php
/**
* #package Joomla.Site
* #subpackage mod_menu
*
* #copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* #license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Helper for mod_menu
*
* #package Joomla.Site
* #subpackage mod_menu
* #since 1.5
*/
class ModMenuHelper
{
/**
* Get a list of the menu items.
*
* #param JRegistry $params The module options.
*
* #return array
*
* #since 1.5
*/
public static function getList(&$params)
{
$app = JFactory::getApplication();
$menu = $app->getMenu();
// Get active menu item
$base = self::getBase($params);
$user = JFactory::getUser();
$levels = $user->getAuthorisedViewLevels();
asort($levels);
$key = 'menu_items' . $params . implode(',', $levels) . '.' . $base->id;
$cache = JFactory::getCache('mod_menu', '');
if (!($items = $cache->get($key)))
{
$path = $base->tree;
$start = (int) $params->get('startLevel');
$end = (int) $params->get('endLevel');
$showAll = $params->get('showAllChildren');
$items = $menu->getItems('menutype', $params->get('menutype'));
$lastitem = 0;
if ($items)
{
foreach ($items as $i => $item)
{
if (($start && $start > $item->level)
|| ($end && $item->level > $end)
|| (!$showAll && $item->level > 1 && !in_array($item->parent_id, $path))
//THIS IS LINE 59 || ($start > 1 && !in_array($item->tree[$start - 2], $path)))
{
unset($items[$i]);
continue;
}
$item->deeper = false;
$item->shallower = false;
$item->level_diff = 0;
if (isset($items[$lastitem]))
{
$items[$lastitem]->deeper = ($item->level > $items[$lastitem]->level);
$items[$lastitem]->shallower = ($item->level < $items[$lastitem]->level);
$items[$lastitem]->level_diff = ($items[$lastitem]->level - $item->level);
}
$item->parent = (boolean) $menu->getItems('parent_id', (int) $item->id, true);
$lastitem = $i;
$item->active = false;
$item->flink = $item->link;
// Reverted back for CMS version 2.5.6
switch ($item->type)
{
case 'separator':
case 'heading':
// No further action needed.
continue;
case 'url':
if ((strpos($item->link, 'index.php?') === 0) && (strpos($item->link, 'Itemid=') === false))
{
// If this is an internal Joomla link, ensure the Itemid is set.
$item->flink = $item->link . '&Itemid=' . $item->id;
}
break;
case 'alias':
// If this is an alias use the item id stored in the parameters to make the link.
$item->flink = 'index.php?Itemid=' . $item->params->get('aliasoptions');
break;
default:
$router = JSite::getRouter();
if ($router->getMode() == JROUTER_MODE_SEF)
{
$item->flink = 'index.php?Itemid=' . $item->id;
}
else
{
$item->flink .= '&Itemid=' . $item->id;
}
break;
}
if (strcasecmp(substr($item->flink, 0, 4), 'http') && (strpos($item->flink, 'index.php?') !== false))
{
$item->flink = JRoute::_($item->flink, true, $item->params->get('secure'));
}
else
{
$item->flink = JRoute::_($item->flink);
}
// We prevent the double encoding because for some reason the $item is shared for menu modules and we get double encoding
// when the cause of that is found the argument should be removed
$item->title = htmlspecialchars($item->title, ENT_COMPAT, 'UTF-8', false);
$item->anchor_css = htmlspecialchars($item->params->get('menu-anchor_css', ''), ENT_COMPAT, 'UTF-8', false);
$item->anchor_title = htmlspecialchars($item->params->get('menu-anchor_title', ''), ENT_COMPAT, 'UTF-8', false);
$item->menu_image = $item->params->get('menu_image', '') ? htmlspecialchars($item->params->get('menu_image', ''), ENT_COMPAT, 'UTF-8', false) : '';
}
if (isset($items[$lastitem]))
{
$items[$lastitem]->deeper = (($start?$start:1) > $items[$lastitem]->level);
$items[$lastitem]->shallower = (($start?$start:1) < $items[$lastitem]->level);
$items[$lastitem]->level_diff = ($items[$lastitem]->level - ($start?$start:1));
}
}
$cache->store($items, $key);
}
return $items;
}
/**
* Get base menu item.
*
* #param JRegistry $params The module options.
*
* #return object
*
* #since 3.0.2
*/
public static function getBase(&$params)
{
// Get base menu item from parameters
if ($params->get('base'))
{
$base = JFactory::getApplication()->getMenu()->getItem($params->get('base'));
}
else
{
$base = false;
}
// Use active menu item if no base found
if (!$base)
{
$base = self::getActive($params);
}
return $base;
}
/**
* Get active menu item.
*
* #param JRegistry $params The module options.
*
* #return object
*
* #since 3.0.2
*/
public static function getActive(&$params)
{
$menu = JFactory::getApplication()->getMenu();
return $menu->getActive() ? $menu->getActive() : $menu->getDefault();
}
}
Change it to:
|| ($start > 1 && !empty($item->tree[$start - 2]) && !in_array($item->tree[$start - 2], $path)))
empty() will check for existence and that it isn't empty at the same time.

Get class name from file

I have a php file which contains only one class. how can I know what class is there by knowing the filename? I know I can do something with regexp matching but is there a standard php way? (the file is already included in the page that is trying to figure out the class name).
There are multiple possible solutions to this problem, each with their advantages and disadvantages. Here they are, it's up to know to decide which one you want.
Tokenizer
This method uses the tokenizer and reads parts of the file until it finds a class definition.
Advantages
Do not have to parse the file entirely
Fast (reads the beginning of the file only)
Little to no chance of false positives
Disadvantages
Longest solution
Code
$fp = fopen($file, 'r');
$class = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
$tokens = token_get_all($buffer);
if (strpos($buffer, '{') === false) continue;
for (;$i<count($tokens);$i++) {
if ($tokens[$i][0] === T_CLASS) {
for ($j=$i+1;$j<count($tokens);$j++) {
if ($tokens[$j] === '{') {
$class = $tokens[$i+2][1];
}
}
}
}
}
Regular expressions
Use regular expressions to parse the beginning of the file, until a class definition is found.
Advantages
Do not have to parse the file entirely
Fast (reads the beginning of the file only)
Disadvantages
High chances of false positives (e.g.: echo "class Foo {";)
Code
$fp = fopen($file, 'r');
$class = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
if (preg_match('/class\s+(\w+)(.*)?\{/', $buffer, $matches)) {
$class = $matches[1];
break;
}
}
Note: The regex can probably be improved, but no regex alone can do this perfectly.
Get list of declared classes
This method uses get_declared_classes() and look for the first class defined after an include.
Advantages
Shortest solution
No chance of false positive
Disadvantages
Have to load the entire file
Have to load the entire list of classes in memory twice
Have to load the class definition in memory
Code
$classes = get_declared_classes();
include 'test2.php';
$diff = array_diff(get_declared_classes(), $classes);
$class = reset($diff);
Note: You cannot simply do end() as others suggested. If the class includes another class, you will get a wrong result.
This is the Tokenizer solution, modified to include a $namespace variable containing the class namespace, if applicable:
$fp = fopen($file, 'r');
$class = $namespace = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
$tokens = token_get_all($buffer);
if (strpos($buffer, '{') === false) continue;
for (;$i<count($tokens);$i++) {
if ($tokens[$i][0] === T_NAMESPACE) {
for ($j=$i+1;$j<count($tokens); $j++) {
if ($tokens[$j][0] === T_STRING) {
$namespace .= '\\'.$tokens[$j][1];
} else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
break;
}
}
}
if ($tokens[$i][0] === T_CLASS) {
for ($j=$i+1;$j<count($tokens);$j++) {
if ($tokens[$j] === '{') {
$class = $tokens[$i+2][1];
}
}
}
}
}
Say you have this class:
namespace foo\bar {
class hello { }
}
...or the alternative syntax:
namespace foo\bar;
class hello { }
You should have the following result:
var_dump($namespace); // \foo\bar
var_dump($class); // hello
You could also use the above to detect the namespace a file declares, regardless of it containing a class or not.
You can make PHP do the work by just including the file and get the last declared class:
$file = 'class.php'; # contains class Foo
include($file);
$classes = get_declared_classes();
$class = end($classes);
echo $class; # Foo
If you need to isolate that, wrap it into a commandline script and execute it via shell_exec:
$file = 'class.php'; # contains class Foo
$class = shell_exec("php -r \"include('$file'); echo end(get_declared_classes());\"");
echo $class; # Foo
If you dislike commandline scripts, you can do it like in this question, however that code does not reflect namespaces.
I modified Nette\Reflection\AnnotationsParser that so it returns an array of namespace+classname that are defined in the file
$parser = new PhpParser();
$parser->extractPhpClasses('src/Path/To/File.php');
class PhpParser
{
public function extractPhpClasses(string $path)
{
$code = file_get_contents($path);
$tokens = #token_get_all($code);
$namespace = $class = $classLevel = $level = NULL;
$classes = [];
while (list(, $token) = each($tokens)) {
switch (is_array($token) ? $token[0] : $token) {
case T_NAMESPACE:
$namespace = ltrim($this->fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\\', '\\');
break;
case T_CLASS:
case T_INTERFACE:
if ($name = $this->fetch($tokens, T_STRING)) {
$classes[] = $namespace . $name;
}
break;
}
}
return $classes;
}
private function fetch(&$tokens, $take)
{
$res = NULL;
while ($token = current($tokens)) {
list($token, $s) = is_array($token) ? $token : [$token, $token];
if (in_array($token, (array) $take, TRUE)) {
$res .= $s;
} elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], TRUE)) {
break;
}
next($tokens);
}
return $res;
}
}
$st = get_declared_classes();
include "classes.php"; //one or more classes in file, contains class class1, class2, etc...
$res = array_values(array_diff_key(get_declared_classes(),$st));
print_r($res); # Array ([0] => class1 [1] => class2 [2] ...)
Thanks to some people from Stackoverflow and Github, I was able to write this amazing fully working solution:
/**
* get the full name (name \ namespace) of a class from its file path
* result example: (string) "I\Am\The\Namespace\Of\This\Class"
*
* #param $filePathName
*
* #return string
*/
public function getClassFullNameFromFile($filePathName)
{
return $this->getClassNamespaceFromFile($filePathName) . '\\' . $this->getClassNameFromFile($filePathName);
}
/**
* build and return an object of a class from its file path
*
* #param $filePathName
*
* #return mixed
*/
public function getClassObjectFromFile($filePathName)
{
$classString = $this->getClassFullNameFromFile($filePathName);
$object = new $classString;
return $object;
}
/**
* get the class namespace form file path using token
*
* #param $filePathName
*
* #return null|string
*/
protected function getClassNamespaceFromFile($filePathName)
{
$src = file_get_contents($filePathName);
$tokens = token_get_all($src);
$count = count($tokens);
$i = 0;
$namespace = '';
$namespace_ok = false;
while ($i < $count) {
$token = $tokens[$i];
if (is_array($token) && $token[0] === T_NAMESPACE) {
// Found namespace declaration
while (++$i < $count) {
if ($tokens[$i] === ';') {
$namespace_ok = true;
$namespace = trim($namespace);
break;
}
$namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
}
break;
}
$i++;
}
if (!$namespace_ok) {
return null;
} else {
return $namespace;
}
}
/**
* get the class name form file path using token
*
* #param $filePathName
*
* #return mixed
*/
protected function getClassNameFromFile($filePathName)
{
$php_code = file_get_contents($filePathName);
$classes = array();
$tokens = token_get_all($php_code);
$count = count($tokens);
for ($i = 2; $i < $count; $i++) {
if ($tokens[$i - 2][0] == T_CLASS
&& $tokens[$i - 1][0] == T_WHITESPACE
&& $tokens[$i][0] == T_STRING
) {
$class_name = $tokens[$i][1];
$classes[] = $class_name;
}
}
return $classes[0];
}
You can do this in two ways:
complex solution: open the file and through regex extract the class-name (like /class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/)
simply solution: name all your php files with the class-name contained (eg: the class TestFoo in the file TestFoo.php or TestFoo.class.php)
You could get all declared classes before you include the file using get_declared_classes. Do the same thing after you have included it and compare the two with something like array_diff and you have your newly added class.
This sample returns all classes. If you're looking for a class which is derived of a specific one, use is_subclass_of
$php_code = file_get_contents ( $file );
$classes = array ();
$namespace="";
$tokens = token_get_all ( $php_code );
$count = count ( $tokens );
for($i = 0; $i < $count; $i ++)
{
if ($tokens[$i][0]===T_NAMESPACE)
{
for ($j=$i+1;$j<$count;++$j)
{
if ($tokens[$j][0]===T_STRING)
$namespace.="\\".$tokens[$j][1];
elseif ($tokens[$j]==='{' or $tokens[$j]===';')
break;
}
}
if ($tokens[$i][0]===T_CLASS)
{
for ($j=$i+1;$j<$count;++$j)
if ($tokens[$j]==='{')
{
$classes[]=$namespace."\\".$tokens[$i+2][1];
}
}
}
return $classes;
I spent lots of productive time looking for a way around this.
From #netcoder's solution its obvious there are lots of cons in all the solutions so far.
So I decided to do this instead.
Since most PHP classes has class name same as filename, we could get the class name from the filename. Depending on your project you could also have a naming convention.
NB: This assume class does not have namespace
<?php
$path = '/path/to/a/class/file.php';
include $path;
/*get filename without extension which is the classname*/
$classname = pathinfo(basename($path), PATHINFO_FILENAME);
/* you can do all this*/
$classObj = new $classname();
/*dough the file name is classname you can still*/
get_class($classObj); //still return classname
Let me add a PHP 8 compatible solution as well. This will scan a file accordingly and return all FQCN's:
$file = 'whatever.php';
$classes = [];
$namespace = '';
$tokens = PhpToken::tokenize(file_get_contents($file));
for ($i = 0; $i < count($tokens); $i++) {
if ($tokens[$i]->getTokenName() === 'T_NAMESPACE') {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j]->getTokenName() === 'T_NAME_QUALIFIED') {
$namespace = $tokens[$j]->text;
break;
}
}
}
if ($tokens[$i]->getTokenName() === 'T_CLASS') {
for ($j = $i + 1; $j < count($tokens); $j++) {
if ($tokens[$j]->getTokenName() === 'T_WHITESPACE') {
continue;
}
if ($tokens[$j]->getTokenName() === 'T_STRING') {
$classes[] = $namespace . '\\' . $tokens[$j]->text;
} else {
break;
}
}
}
}
// Contains all FQCNs found in a file.
$classes;
You may be able to use the autoload function.
function __autoload($class_name) {
include "special_directory/" .$class_name . '.php';
}
And you can echo $class_name. But that requires a directory with a single file.
But it is standard practice to have one class in each file in PHP. So Main.class.php will contain Main class. You may able to use that standard if you are the one coding.

Categories