I downloaded the PHP Amazon MWS Reports Client Library from here:
https://developer.amazonservices.com/gp/mws/api.html/182-5103998-0984662?ie=UTF8&group=bde§ion=reports&version=latest
and I was trying to get it to work but it looks like the library is not complete out of the box or there is something that I don't fully understand. For example in the samples folder there are sample functions that should let you get up to speed in no time however when you run any of them, PHP complains about missing classes. Lets take at one of the top lines of one of them:
$service = new MarketplaceWebService_Client(
'xyz',
'xyz',
$config,
'xyz',
'1.0');
So it is instancing the MarketplaceWebService_Client however that class is neither attached to this file nor nowhere to be found inside of it. After a quick search I found that the function exist under the following hierarchy:
MarketplaceWebService/Client.php
Can you see the resemblance to the class name? How is that supposed to work? Should I add all of those files using require_once or there should be any mechanism that loads them automatically?
Another one: class MarketplaceWebService_Model_GetReportListRequest exist under
MarketplaceWebService/Model/GetReportListRequest.php
I know that I could create an __autoload function and simply attach those classes dynamically but is this what the author had in mind?
PHP has an autoload capability which enables a function to be called if a required class does not exist at runtime. This capability allows the script to go and produce the missing class, normally by including a file which contains it.
Here's an example adapted from the PHP manual.
// Your missing class is called MarketplaceWebService_Client
// The code for this class is in MarketplaceWebService/Client.php
// define a function that will be called when a class does not yet exist
function my_autoloader($class) {
// implement the rules to convert the class into the file naming convention
$path = str_replace('_', '/', $class) . 'php';
// if there is a match, then include it now
if(file_exists($path)) {
include_once $path;
}
}
// tell PHP about the autoload function
spl_autoload_register('my_autoloader');
You may need to tweak the above example to fit your specific code and folder structure.
I can't speak to the PHP client library, but the C# library is complete out of the box and compiles from the start. The MWS team has a dedicated contact us page for these type of issues. They are willing to work with you through your issues and get you moving. You do have to log in with your seller credentials to access this page. Give it a try.
https://sellercentral.amazon.com/gp/mws/contactus.html
Related
This is my project path configuration
./create.php
/Install/Install.php
create.php
<?php
use Install\Install;
echo "Starting";
$install = new Install();
This gives me the error
PHP Fatal error: Uncaught Error: Class 'Install\Install' not found in /project/create.php:6
Install.php
<?php
namespace Install;
class Install
{
//whatever
}
Can someone explain me what is happening there ?
Obviously I guess that using a require_once line with my filename would probably fix the issue...but I thought using namespace and use import could prevent me from doing that like we do in classic framework like symfony / magento ?
I've seen some post speaking about autoloading, but i'm a little bit lost. Haven't been able to find a clear explanation on the other stack topic neither.
PHP compiles code one file at a time. It doesn't have any native concept of a "project" or a "full program".
There are three concepts involved here, which complement rather than replacing each other:
Namespaces are just a way of naming things. They allow you to have two classes called Install and still tell the difference between them. The use statement just tells the compiler (within one file) which of those classes you want when you write Install. The PHP manual has a chapter on namespaces which goes into more detail on all of this.
Require and include are the only mechanisms that allow code in one file to reference code in another. At some point, you need to tell the compiler to load "Install.php".
Autoloading is a way for PHP to ask your code which file it should load, when you mention a class it hasn't seen the definition for yet. The first time a class name is encountered, any function registered with spl_autoload_register will be called with that class name, and then has a chance to run include/require to load the definition. There is a fairly brief overview of autoloading in the PHP manual.
So, in your example:
use Install\Install; just means "when I write Install, I really mean Install\Install"
new Install() is translated by the compiler to new Install\Install()
the class Install\Install hasn't been defined; if an autoload function has been registered, it will be called, with the string "Install\Install" as input
that autoload function can then look at that class name, and run require_once __DIR__ . '/some/path/Install.php';
You can write the autoload function yourself, or you can use an "off-the-shelf" implementation where you just have to configure the directory where your classes are, and then follow a convention for how to name them.
If you want to Use class from another file, you must include or require the file.
Use require('Install.php'); before use Install\Install;.
If you are planning to do a big project I would recommend to use PHP frameworks rather than coding from scratch.
I am creating a little CLI tool to assist in using a PHP library I built. One of the requirements of this tool is to Generate custom classes and interfaces, from a STUB template, all from a CLI command.
However, I am having a bit of problems with generating namespaces correctly.
So, I have this one stub file that looks like this.
<?php
namespace DummyNamspace;
use Spheracle\ApplicationService\ApplicationServiceInterface;
interface DummyContextNameServiceInterface extends ApplicationServiceInterface
{
// your code goes here
}
?>
I am using this library to create the stubs.
And, in my command class (Symfony), I have this code to create the namespace, which is passed to the generator directly.
$ns = ($project) ? $project . "\\" . $context : $context;
$generator = new AppServiceInterfaceGenerator($context, $dir, $ns);
$generator->generate();
here, $ms is the namespace text that will be placed in the STUB file. However, when I run this code via its assigned CLI command, I get the following output in the generated file.
My expected namespace for this entry is `Foo\Bar'. However, I get the following.
<?php
namespace 'Bar'Namspace;
use Spheracle\ApplicationService\ApplicationServiceInterface;
interface 'Bar'ContextNameServiceInterface extends ApplicationServiceInterface
{
// your code goes here
}
?>
Any suggestions what I am doing wrong? I assume the issue with the interface name would be resolved once the namespace issue is resolved. I have a feeling the problem might have something to do with the underlying library I am using to generate the classes. But, I want to get other's option to double check my work before I start searching for a better stub generation library.
Thanks.
I have the code below. When I run it I get error:
Fatal error: Cannot redeclare class Google_Account in
/var/www/vhosts/example.com/httpdocs/google-api-php-client
/src/contrib/Google_AnalyticsService.php on line 379
That's because both of the "Google_AdsenseService.php" and "Google_AnalyticsService.php" files have a class named Google_Account. The member variables and functions of Google_Account class are different in that files.
I need to get Adsense and Analytics data in the same time. So I need to use both of the services at once. I couldn't find a way to un-declare classes. How can I use both of the services together?
include_once APP.'Vendor/google-api-php-client/src/Google_Client.php';
$client1 = new Google_Client();
$client1->setApplicationName('aaa');
$client1->setDeveloperKey('1234');
$client1->setRedirectUri('http://example.com/');
include_once APP.'Vendor/google-api-php-client/src/contrib/Google_AdsenseService.php';
$client1->setClientId('2345');
$client1->setClientSecret('4444');
$service1 = new Google_AdsenseService($client1);
// some code that gets data from "$service1"
$client2 = new Google_Client();
$client2->setApplicationName('aaa');
$client2->setDeveloperKey('1234');
$client2->setRedirectUri('http://example.com/');
include_once APP.'Vendor/google-api-php-client/src/contrib/Google_AnalyticsService.php';
$client2->setClientId('4567');
$client2->setClientSecret('5555');
$service2 = new Google_AnalyticsService($client2);
// some code that gets data from "$service2"
You can add different namespaces at the top of each files in contrib directory. For example for Google_AdsenseService.php file add namespace Google\AdsenseService; at the top.
// Google_AdsenseService.php file
namespace Google\AdsenseService;
As long as the file contents are only referencing the contents from same file it'll will work. Only when you access it you access by namespace. Like this,
$service1 = new Google\AdsenseService\Google_AdsenseService($client1);
You have two options:
For PHP 5.3+, you can add a namespace at the start of the file. After this, you need to fix references to other classes from the modified class (Exception will become ::Exception, etc.)
You may rename the class in a text editor, this will be probably easier. Just open up the file in your favorite text editor, and use replace all. Change Google_Client to something else. There is a good change the lib won't use dynamic class construction and other funny stuff, so your quickly refactored code will work.
I have asked a similar question to this one already but I think it was badly worded and confusing so hopefully I can make it a bit clearer.
I am programming in a native Linux file system.
I have a class of HelpTopic:
class HelpTopic extends Help{}
And a class of Help:
class Help{}
Now I go to include HelpTopic:
include('HelpTopic.php');
And even though I do not instantiate HelpTopic with new HelpTopic() PHP (in a Linux file system) still reads the class signature and tries to load Help with HelpTopic.
I do not get this behaviour from a cifs file system shared from a Windows System.
My best guess is that there is some oddity with Linux that causes PHP to react this way but not sure what.
Does anyone have any ideas or solutions to this problem?
EDIT:
I have added my loading function to show what I am doing:
public static function import($cName, $cPath = null){
if(substr($cName, -2) == "/*"){
$d_name = ROOT.'/'.substr($cName, 0, -2);
$d_files = getDirectoryFileList($d_name, array("\.php")); // Currently only accepts .php
foreach($d_files as $file){
glue::import(substr($file, 0, strrpos($file, '.')), substr($cName, 0, -2).'/'.$file);
}
}else{
if(!$cPath) $cPath = self::$_classMapper[$cName];
if(!isset(self::$_classLoaded[$cName])){
self::$_classLoaded[$cName] = true;
if($cPath[0] == "/" || preg_match("/^application/i", $cPath) > 0 || preg_match("/^glue/i", $cPath) > 0){
return include ROOT.'/'.$cPath;
}else{
return include $cPath;
}
}
return true;
}
}
I call this by doing glue::inmport('application/models/*'); and it goes through including all the models in my app. Thing is PHP on a linux based file system (not on cifs) is trying to load the parents of my classes without instantiation.
This is a pretty base function that exists in most frameworks (in fact most of this code is based off of yiis version) so I am confused why others have not run into this problem.
And even though I do not instantiate HelpTopic with new HelpTopic() PHP still reads the class signature and tries to load Help with HelpTopic.
Correct.
In order to know how to properly define a class, PHP needs to resolve any parent classes (all the way up) and any interfaces. This is done when the class is defined, not when the class is used.
You should probably review the PHP documentation on inheritance, which includes a note explaining this behavior:
Unless autoloading is used, then classes must be defined before they are used. If a class extends another, then the parent class must be declared before the child class structure. This rule applies to class that inherit other classes and interfaces.
There are two ways to resolve this problem.
First, add a require_once at the top of the file that defines the child class that includes the file defining the parent class. This is the most simple and straight-forward way, unless you have an autoloader.
The second way is to defione an autoloader. This is also covered in the documentation.
The ... thing ... you're using there is not an autoloader. In fact, it's a horrible abomination that you should purge from your codebase. It's a performance sap and you should not be using it. It also happens to be the thing at fault.
We don't have the definition of getDirectoryFileList() here, so I'll assume it uses either glob() or a DirectoryIterator. This is the source of your problem. You're getting the file list in an undefined order. Or, rather, in whatever order the underlying filesystem wants to give to you. On one machine, the filesystem is probably giving you Help.php before HelpTopic.php, while on the other machine, HelpTopic.php is seen first.
At first glance, you might think this is fixable with a simple sort, but it's not. What happens if you create a Zebra class, and then later need to create an AlbinoZebra that inherits from it? No amount of directory sorting is going to satisfy both the "load ASCIIbetical" and the "I need the Zebra to be first" requirements.
Let's also touch on the performance aspect of the problem. On every single request, you're opening a directory and reading the list of files. That's one hell of a lot of stat calls. This is slow. Very slow. Then, one by one, regardless of whether or not you'll need them, you're including the files. This means that PHP has to compile and interpret every single one of them. If you aren't using a bytecode cache, this is going to utterly destroy performance if the number of files there ever grows to a non-trivial number.
A properly constructed autoloader will entirely mitigate this problem. Autoloaders run on demand, meaning that they'll never attempt to include a file before it's actually needed. Good-performing autoloaders will know where the class file lives based on the name alone. In modern PHP, it's accepted practice to name your classes such that they'll be found easily by an autoloader, using either namespaces or underscores -- or both -- to map directory separators. (Meaning namespace \Models; class Help or class Models_Help would live in Models/Help.php)
Unfortunately most examples won't be useful here, as I don't know what kind of weird things your custom framework does. Take a peek at the Zend Framework autoloader, which uses prefix registration to point class prefixes (Model_) at directories.
So I have an idea, but I'm thinking I need to run it by StackOverflow before I do something stupid.
I want to have an associative array of class names => filepaths. If PHP ever runs into a fatal error where the class is not defined, it will check if the key exists in my array and then require_once the class. This will prevent unnecessary bulk loading of classes that may never be used.
Bad idea?
How about trying PHP's built in autoloading.
Autoloading is the right way to do it, but spl_autoload_register is a cleaner way than __autoload, because it allows multiple autoloaders. Function __autoload also AFAIK stops working when spl_autoload_register is called, unless __autoload is also registered.
You can write your own autoload or use an existing one. For example, Zend Framework has an autoloader that uses conventions (Foo_Bar is in Foo/Bar.php). Nette Framework has RobotLoader, that indexes your classes and uses the index when neccessary. However, unless you use other things from the framework, it is probably too large.
see: http://www.php.net/manual/en/function.spl-autoload-register.php
If you are on PHP5, you can use __autoload().
makes your code a bit more manageable , although performance-wise, it's a bad choice. But I wouldn't worry it unless I'm building a Facebook.
What you are trying to do is already handled by the php __autoload function. You can read all about it here: http://php.net/manual/en/language.oop5.autoload.php
So, not a bad idea at all ;)
you should use autoloading with specified clas name structure, here is an example
the class names should should be only alpha and _ case-insensitive.
Lets take this directory structure and files
/classes/class.php
/classes/input/input.php
/classes/output/output.php
/classes/regex/regex.php
/classes/interface/parser/interface_parser.php
/classes/parser/parser.php
/classes/parser/xml/parser_xml.php
/classes/parser/html/parser_html.php
having the structure like this is good as it encourages you to code better when it comes to OOP.
Now if we take a look at the /classes/parser/html/html_parser.php file:
class Parser_Html extends Parser implements Interface_Parser
{
//looks nice in here
}
usually you would have to make sure the interface and the extended class is loaded, but these get autoloaded as well if they have not already.
creating the auto load system for this is not that complex, its just 1 function.
function __autoload($name)
{
//Classes
$parts = explode('_',strtolower($name));
$path = '/classes/';
foreach($parts as $p)
{
$path .= $p;
}
$path .= '/' . $name . '.php';
if(file_exists($path))
{
require_once $path;
}
}
so instead of including the class file first just run the class initiation.
$HtmlParser = new Parser_Html();
as the file has not been include the __autoload is run with a param of the class name, the autoload then looks in the directory that's relevant to the class name to try and load it.
also as your using the extend keyword in the class file shown above the class that is to be the parent gets run threw trhe autoloader aswell so you do not need to pre-load interfaces and classes etc.
Hope this helps you.
Note:
All code provided is untested and written for informational purposes, I would recommend you research the techniques more in detail before any implementation is done.