I'm struggling with this for a while now and since Google has tons of results on this issue i'm wondering what i'm doing wrong since none of the solutions seems to work for me.
I have two classes File and Image. I let the File class decide wether the input is an image or another type of file. When the file is an image i want to pass that file to the Image class to process it.
So far i have this
Class File{
public $file;
function __construct($input){
$this->file = $input;
}
public function getFileType(){
// determine filetype of $this->file
return 'image';
}
}
Class Image Extends File{
function __construct(){}
public function test(){
return $this->file;
}
}
$file = new File('file.jpg');
if($file->getFileType() == 'image'){
$image = new Image;
echo $image->test();
}
But this doesn't output anything. How can i access the value of the constructor argument of the parent class in the inherited class? Calling parent::__construct(); in the child constructor class (as mentioned here) gives me a missing argument warning and this one (call_user_func_array(array($this, 'parent::__construct'), $args); in the child constructor) also doesn't work.
What am i missing?
First you need to understand that $image and $file in your code are 2 different objects.
$image knows nothing about $file and vice versa.
With your code design the solution can be:
Class File {
public $file;
function __construct($input){
$this->file = $input;
}
public function getFileType(){
// determine filetype of $this->file
return 'image';
}
}
Class Image Extends File{
function __construct($input)
{
parent::__construct($input);
// after that you have `$this->file` set
}
public function test(){
return $this->file;
}
}
$file = new Image('file.jpg');
if ($file->getFileType() == 'image'){
echo $file->test();
}
But such approach is messy. You create object of class Image and after creation you make sure that it is really image. I suppose you need to use something like fabric pattern and generate object of proper kind in a File class.
Related
I am trying to get into the world of OOP and therefore building my first class out of some file handler functions. I am having a hard time understanding the whole idea of objects and methods, and I am not quite sure if I did it the right way, though the output is as expected.
I would appreciate your help with the following example. How could I add a method to it the right way?
class.File.php
class File {
public $file = '';
public $data = '';
public function __construct($file = '') {
$this->file = $file;
}
function put($create = false) {
// Check if file is writeable and put content, won't create new file unsless $create is set to true
if($create == false) {
is_writable($this->file) ? file_put_contents($this->file, $this->data, LOCK_EX) : exit;
} else {
file_put_contents($this->file, $this->data);
}
}
}
example call
$f = new File();
$f->file = "/www/htdocs/somehost/folder/data/helloworld.txt";
$f->data = "Hello world";
$f->put('true');
Object is the concrete entity of the real world. That has properties and behaviour (methods).
What are the properties of the concrete file? It is a name, permissions, a date of creation, etc. All properties should be hidden from real world.
What behaviour can the file have? Read content, rewrite content, append content, rename, delete, change permissions, etc. All that you do with the file. Note it is better to have two methods "rewrite content" and "append content" than one "put" with arguments. Because these are different actions on the file.
So let's write the class File.
class File
{
private $name;
private $permissions;
// name and permissions are mandatory because file cannot exist without these properties
public function __construct($name, $permissions)
{
$this->name = $name;
$this->permissions = $permissions;
}
public function read()
{
// you can check if the file exists
return file_get_contents($this->name);
}
// may also named `put`
public function write($content)
{
file_put_contents($this->name, $content);
}
public function append($content)
{
file_put_contents($this->name, $content, FILE_APPEND);
}
public function rename($name)
{
$this->name = $name;
}
}
Any checks (if file exists, if file is writeble) is concrete implementation, not about OOP.
PS
You can read this article http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html about getters and setters.
Pardon any mistakes in verbiage, I am trying to learn classes.
Once an object has been instantiated, I understand that methods from that class become available to me. My question is, how am I able to run methods from a class other than the instantiated one on said object?
Specifically:
class image {
public static function create() {
$image = new Imagick($file);
$image -> image::autoRotate($image);
...
}
public static function autoRotate($image) {
...
}
}
the line $image -> image::autoRotate($image) yields the error, and I understand the syntax and/or my understanding are at fault. Can someone kindly help me understand how to accomplish this please?
Because the the image class isn't actually a property of the $image object, you don't need to use the $image -> syntax to perform that operation. Since autoRotate() is a static function, it can be called just from the class accessor image::autoRotate($image);
class image {
public static function create() {
$image = new Imagick($file);
image::autoRotate($image); // removed $image ->
...
}
public static function autoRotate($image) {
...
}
}
public static function can be called directly by classname::funcname, no need to instantiate an object first. In your case:
class image {
public static function create() {
$image = new Imagick($file);
image::autoRotate($image);
...
}
public static function autoRotate($image) {
...
}
}
Try to replace line ..
$image -> image::autoRotate($image);
with this one..
self::autoRotateImage($image);
I am realy new to PHPUnit, and for my first self-made test case, I would like to check if a website has an image, by providing a link to PHPUnit like this.
lets call link I want to test http://somedomain.com/images/images.jpg
Now, I wrote this functions, and I know I am probably doing it very wrong, but that is why I am asking it here.
class WebsiteHasImage{
public function images(){
$website = 'http://somedomain.com/images/img.jpg';
return $website;
}
}
class WebsiteHasImageTest extends PHPUnit_Framework_TestCase
{
public function testSuccess(){
$this->assertTrue($this->images(), TRUE);
}
}
Call to undefined method WebsiteHasImageTest::images() in ... on line 15
line 15 is this.
$this->assertTrue($this->images(), TRUE);
Your class will return a string for the images() which is not what you are really testing.
class WebsiteHasImage{
public function images(){
$website = 'http://somedomain.com/images/img.jpg';
return $website;
}
}
class WebsiteHasImageTest extends PHPUnit_Framework_TestCase
{
public function testSuccess()
{
WebSiteImage = new WebSiteHasImage();
$this->assertEquals('http://somedomain.com/images/img.jpg', $WebSiteImage->images(), 'Check returned File Name is correct');
}
}
All this has done is test that the image string is correct.
You could try and read the image with another call, and then compare it, but that would require that your website is up.
Optionally, the call could look into the current application directory for images/img.jpg and do some tests (byte size is proper, etc...) to validate an image.
You could attempt to read the website and return the contents, but again, that requires the website. So to test your code, you have a class that returns the website data, and then mock that class to return a fixed string, that you would then write tests against to see that the code in your class does extract the images correctly. Your class could then test the image exists, and again, mock that to always return it does to test what your code does.
public function GetWebPageContents($HTTPAddress)
{
$HTML = ... // Code that reads the contents
return $HTML;
}
public function GetImageList($HTML)
{
$ImageList = array();
... // Code to find all images, adding to ImageList
return $ImageList;
}
Test class additions
public function testGetWebPageContents()
{
$HTMLText = '<html><body><img scr=""></img></body><</html>'; // Fill this with a sample webpage text
$MockClass = $this->getMock('WebsiteHasImage', array('GetWebPageContents'));
// Set up the expectation for the GetWebPageContents() method
$MockClass->expects($this->any())
->method('GetWebPageContents')
->will($this->returnValue($HMTLText));
// Create Test Object
$TestClass = new MockClass();
$this->assertEquals($HTMLText, $TestClass->GetWebPageContents('http://testaddress.com'), 'Validate Mock HTML return'); // Address is garbage as it does not matter since we mocked it
$WebsiteTest = new WebsiteHasImage();
$ImageList = $WebsiteTest->GetImageList($HTMLText);
$this->assertEquals(1, count($ImageList), 'Count Total Number of Images');
$this->assertEquals('images/img.jpg', $ImageList[0], 'Check actual image file name from dummy HTML parsing');
... // More tests for contents
}
Now, the best change is to use the read data within your class, and for that, you then pass in the HTML code to the class normally after reading it from the website. In your test, you would then pass the object that does the reading in using the Mock object in the constructor, or via a Set. Do a Google search for Dependency Injection, Mock Objects, etc... to find more information on this topic.
I havent worked with PHPunit myself but looking at your code $this->images() wont work because you don't have a images method in the WebsiteHasImageTest class.
<?php
class WebsiteHasImageTest extends PHPUnit_Framework_TestCase
{
public function images(){
$website = 'http://somedomain.com/images/img.jpg';
return $website;
}
public function testSuccess(){
$this->assertTrue($this->images(), TRUE);
}
}
This will remove the need for WebsiteHasImage, but if it is required you can do.
<?php
class WebsiteHasImage{
public function images(){
$website = 'http://somedomain.com/images/img.jpg';
return $website;
}
}
class WebsiteHasImageTest extends PHPUnit_Framework_TestCase
{
public function testSuccess(){
$images = new WebsiteHasImage();
$this->assertTrue($images->images(), TRUE);
}
}
So it just comes down to:
a) Move your method to the WebsiteHasImageTest class or
b) iniate a new WebsitehasImage() object in your WebsiteHasImageTest testSuccess method.
Well, I don't know if this post have the correct title. Feel free to change it.
Ok, this is my scenario:
pluginA.php
function info(){
return "Plugin A";
}
pluginB.php
function info(){
return "Plugin B";
}
Finally, I have a plugin manager that is in charge of import all plugins info to pool array:
Manager.php
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("pluginA.php", "pluginB.php");
foreach ($plugin_names as $name)
{
include_once $name;
$this->pool[] = info();
}
}
}
The problem here is that when I print pool array it only show me the info on the first plugin loaded. I supposed that the file inclusing override the info because it still calling the info() method from the first include.
Is there a way to include the info of both plugins having the info() function with the same name for all plugins files?
Thank you in advance
PS: a fatal cannot redeclare error is never hurled
you can use the dynamic way to create plugin classes
plugin class
class PluginA
{
public function info()
{
return 'info'; //the plugin info
}
}
manager class
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("pluginA", "pluginB"); //Plugin names
foreach ($plugin_names as $name)
{
$file = $name . '.php';
if(file_exists($file))
{
require_once($file); //please use require_once
$class = new $name(/* parameters ... */); //create new plugin object
//now you can call the info method like: $class->info();
}
}
}
}
Are you sure the interpreter isn't choking w/ a fatal error? It should be since you're trying to define the info function twice here.
There are many ways to achieve what you want, one way as in #David's comment above would be to use classes, eg.
class PluginA
{
function info() { return 'Plugin A'; }
}
class PluginB
{
function info() { return 'Plugin B'; }
}
then the Manager class would be something like this:
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("PluginA", "PluginB");
foreach ($plugin_names as $name)
{
include_once $name . '.php';
$this->pool[] = new $name();
}
}
}
Now you have an instance of each plugin class loaded, so to get the info for a plugin you would have $this->pool[0]->info(); for the first plugin. I would recommend going w/ an associative array though so you can easily reference a given plugin. To do this, the assignment to the pool would become:
$this->pool[$name] = new name();
And then you can say:
$this->pool['PluginA']->info();
for example.
There are many other ways to do it. Now that 5.3 is mainstream you could just as easily namespace your groups of functions, but I would still recommend the associative array for the pool as you can reference a plugin in constant time, rather than linear.
I have created a File class, which takes care of all operations on files, I/O, and which acts differently depending on the nature of the files. I'm not happy with its actual structure, which looks like this:
class File
{
function __construct($id)
{
$bbnq = sprintf("
SELECT *
FROM documents
WHERE id = %u",
$id);
$req = bbnf_query($bbnq);
$bbn = $req->fetch();
$this->file_type = $bbn['file_type'];
$this->file_name = $bbn['file_name'];
$this->title = $bbn['title'];
}
function display()
{
return ''.$this->title.'';
}
}
class Image extends File
{
function __construct($id)
{
global $bbng_imagick;
if ( $bbng_imagick )
$this->imagick = true;
parent::__construct($id);
}
function display()
{
return '<img src="'.$this->file_name.'" alt="'.$this->title.'" />';
}
}
Here I need first to know the file type in order to determine which class/subclass to use.
And I'd like to achieve the opposite, i.e. send an ID to my class, which returns an object corresponding to the file type.
I have recently updated to PHP 5.3, and I know there are some new features which could be of use for creating a "factory" (late static bindings?). My OOP knowledge is pretty light, so I wonder if some have structural suggestions in order to make a unique class which will call the right constructor.
Thanks!
I don't think late static bindings is relevant here - a factory pattern doesn't require them. Try this:
class FileFactory
{
protected static function determineFileType($id)
{
// Replace these with your real file logic
$isImage = ($id>0 && $id%2);
$isFile = ($id>0 && !($id%2));
if ($isImage) return "Image";
elseif ($isFile) return "File";
throw new Exception("Unknown file type for #$id");
}
public static function getFile($id) {
$class = self::determineFileType($id);
return new $class($id);
}
}
// Examples usage(s)
for ($i=3; $i>=0; $i--) {
print_r(FileFactory::getFile($i));
}
As an aside, you should definitely escape your output from the DB, no matter how safe you think it is. Test with double quotes in a title, for example (let alone more malicious input).
Also if it's part of a project, you might want to separate the View layer (your HTML output) from this Model layer, ie implement MVC...
In your factory's constructor, you need to determine the file type, then with that, create an object of the corresponding class. Something like this perhaps:
class File
{
public static function factory($id)
{
$fileData = <query this $id>
switch ($fileData->type) {
case image:
return new ImageFile($fileData);
break;
case html:
return new HtmlFile($fileData);
break;
default:
// error?
}
}
}
abstract class FileAbstract
{
// common file methods here
}
// override the custom bits for each type
class ImageFile extends FileAbstract
{
public function display()
{
// ...
}
}
class HtmlFile extends FileAbstract
{
public function display()
{
// ...
}
}
Your code would then simply be:
$myFile = File::factory($id);
$myFile->display();