I have a PHP object with an id that should not be changed, except in one specific context, when a specific method is called. But this method is outside the object class and can't inherit. I don't know what's the better way to do this properly, how can I "check" if the object setter is called by the right method, to avoid abuses?
Thanks in advance for your help
edit: here is an example:
<?php
class myClass {
private $id;
private $var1;
private $var2;
private function setId($id){
//this must be accessible only when calling "secureSetId()" from class "secureClass"
$this->id = $id;
}
public function setVar1{
//this is a public function...
}
...
}
class secureClass {
private function secureSetId($id){
//this is accessible only here, but how to call private function "setId" from class "myClass"?
}
...
}
?>
<?php
interface SimpleMyClass {
public function setVar1($value);
public function setVar2($value);
}
interface SecureMyClass extends SimpleMyClass {
public function setId($id);
}
class MyClass implements SecureMyClass {
private $id;
private $var1;
private $var2;
public function setVar1($value) { /* ... */ }
public function setVar2($value) { /* ... */ }
public function setId($id) { /* setting id */ }
}
// classes that use "different versions" of MyClass
class SecureOtherClass {
public function setMyClass(SecureMyClass $my) { /* this class is able to set id of MyClass */ }
}
class SimpleOtherClass {
public function setMyClass(SimpleMyClass $my) { /* this class uses simple version of MyClass */ }
}
?>
Related
I have a class which I am going to make Singleton. For this reason, I want to use a trait. Something like this:
trait TSingleton
{
private static $instance = null;
private function __construct() {}
private function __clone() {}
private function __wakeup() {}
/**
* #return static
*/
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static();
}
return static::$instance;
}
}
And then:
class Db implements IDb
{
use TSingleton;
...
}
The question is if the Db's constructor would be private also, and if it does, why could I create its inheritors?
I am creating the dynamic crud, but I want the casting method (database array to object of current class) to be inherited directly to the children I need to get the name the reference this but to the object that I inherited the method, it works perfect if I put it in the class inherited but if I have 10 thousand inherited classes I have to copy 10 thousand times that (that's what I want to avoid)
<?php
namespace Models;
use Interfaces\ICrud;
use db\DBconection;
require('./interfaces/icrud.php');
require('./db/dbconnect.php');
abstract class Model implements ICrud{
private $connection;
protected $schema='public.';
public function __construct(){
}
public function get($id){
return (new DBconection())->runQuery("SELECT * from $this->schema".$this->getModelName(get_class($this))." WHERE id=$id");
}
public function create($entity){
}
public function update($entity){
}
public function delete($id){
return (new DBconection())->runQuery("DELETE from $this->schema".$this->getModelName(get_class($this))." WHERE id=$id");
}
private function getModelName($class){
return strtolower(str_replace('Model','',(new \ReflectionClass($class))->getShortName()));
}
}
this is the child class
<?php
namespace Models;
use Models\Model,
db\DBconection;
require('model.php');
class UsersModel extends Model {
private $id;
private $username;
private $password;
private $active;
private $create;
private $update;
private $id_typeuser;
private $id_people;
protected $schema='security.';
public function __construct(){
}
}
Given I have an abstract class:
abstract class User{
private function getNumber(){
return 'Get number';
}
}
With the child class
class UserComments extends User{
public function rows(){
return $this->getNumberOfRows();
}
}
Question:
Is there any way to call the getNumber function when I try to call getNumberOfRows method in child class, or when I call getNumberOfRows() in child class I want getNumber to be called instead
?
Due to PHP's Object inheritance you can directly call the specific method. However, in your example the getNumberOfRows() function is missing.
class UserComments extends User {
public function rows() {
return $this->getNumber();
}
}
You can do something like this
abstract class User{
private function getNumber(){
return 'Get number';
}
public function getNumberOfRows(){
return $this->getNumber();
}
}
With the child class
class UserComments extends User{
public function rows(){
return $this->getNumberOfRows(); //Defined in the parent class
}
}
Abstract methods cannot be private, abstract must be implemented by the class that derived it.
You can either use public, or if you do not want it to be visible outside, make it protected, as following:
abstract class User{
abstract protected function getNumber();
}
Once you do this, you can implement the getNumber method in User class:
class User {
protected function getNumber() {
// Do something
}
}
Update: Please note that protected methods are accessible by child classes, you can use "hierarchy":
abstract class User{
abstract protected function getNumber();
}
class UserComment extends User {
protected function comment() {
// Do something
}
protected function getNumber() {
return 3;
}
}
class Post extends UserComment {
public function myMethod() {
echo $this->getNumber();
}
}
Also you can use interfaces, just an example:
interface User {
public function getNumber();
}
class UserComment {
protected function myMethod() {
// Do something
}
}
class Post extends UserComment implements User {
final public function getNumber() {
return 3;
}
public function myMethod() {
echo $this->getNumber();
}
}
$post = new Post();
$post->myMethod();
Warning: might cause TL:DR
I am working with PHP 5.3.10 and have the following problem. I do have an abstract class DataMapper, which is extended for the specific DataModel I want to persist. The following code does this trick:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(IModel $model); // DISCUSSION
/* more helper functions here */
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic ... */ }
public function fetchAll() { /* ...magic ... */ }
public function save(IModel $model) { /* ...magic ... */ } // DISCUSSION
}
interface IModel {
public function setOptions(array $options);
public function toArray();
}
abstract class Model implements IModel {
protected $_fields = array();
protected $_data = array();
public function setOptions(array $options) { /* ...magic ... */ }
public function toArray() { /* ...magic ... */ }
public function __construct(array $options = null) { /* ...magic ... */ }
public function __set($name, $value) { /* ...magic ... */ }
public function __get($name) { /* ...magic ... */ }
}
class PersonModel extends Model {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
}
This works fine, but is really quirky for my feeling.
As you can see, I've used an interface IModel to be able to tell the DataMapper, that it does need a certain set of parameters and methods. However, some Models do have extra methods needed by the corresponding DataMapper - in the example, a checkPassword() method, which is used test a password against the stored hash value. This method may also instruct the DataMapper to rehash the just tested password and update it due to new requirements (e.g. an increased difficulty for a password hash function).
So what I actually want is to change the signature of PersonMapper to PersonMapper::save(PersonModel $model) - and e.g. in another DataMapper toPostMapper::save(PostModel $model), etc. This is due to these DataMappers needing a certain signature. So my ideal solution looks like this:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(Model $model); // UPDATED
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) { /* ...magic... */ } // UPDATED
}
abstract class Model { /* ...unchanged... */ }
class PersonModel extends Model { /* ...unchanged... */ }
Notice the Update save-Methods in the abstract class and its implementation. Since PersonModel is inherited from Model, thus obviously having a common base set of signatures, I would expect this to work just fine. But it doesn't - PHP complains about a changed interface in the childclass PersonMapper
My Questions:
Is there another solution working with PHP 5.3.10 that expresses the relationship better?
Does it work in a later version of PHP, so that it might be worth upgrading the server?
You might try using interfaces instead.
interface OtherModel {
public function getThis();
}
interface OtherOtherModel {
public function getThat();
}
Your Model Class might implement one or more interfaces...
class PersonModel extends Model implements OtherModel {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
public function getThis() {
// ...
}
}
Your concrete Mapper Class can use the instanceof to check if this Model does what it should.
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(Model $model) {
// verify that certain methods are implemented...
// throw an exception or reacting accordingly
print ($model instanceof PersonModel)? 'yes' : 'no';
print ($model instanceof OtherOtherModel)? 'yes' : 'no';
}
}
Another possible approach might be the following:
<?php
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public function save(Model $model) {
throw new Exception('You have to implement this!');
}
}
Throw an Exception if the save method is not overriden in an inheriting class.
Now you can really use a different typehint.
This will work:
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) {
// do something
}
}
I could think of another possible approach, by using interfaces to define the implementation.
Like for example:
interface PersonModelAware {
public function save(PersonModel $model);
}
interface OtherModelAware {
public function save(OtherModel $model);
}
etc. Your abstract method might have a default save method or no save method at all. The inheriting class will implement the interface it needs.
To sum it up, making your type more specific will not work as the abstract method clearly states it expects a Model.
in php I have this code. I'm trying to get an inherited method to utilize a member variable of its child class.
abstract class HtmlObj{
//abstract protected function jQuery_Activity();
public $hyperlink;
abstract protected function php_Activity();
abstract protected function print_Widget();
function __construct($hyperlink=""){
if(isset($hyperlink)){
$this->hyperlink = $hyperlink;
}
$this->php_Activity();
$this->Print_Widget();
}
}
class child extends HtmlObj{
public $id;
protected function php_Activity(){return;}
protected function print_Widget(){
print $this->id;
}
function __construct($id){
this->id = $id;
}
}
unfortunately this prints nothing. any insights as to why?
in child class You need to reffer to parent::__construct() by doing something like
abstract class HtmlObj
{
//abstract protected function jQuery_Activity();
public $hyperlink;
abstract protected function php_Activity();
abstract protected function print_Widget();
function __construct($hyperlink = "")
{
if (isset($hyperlink)) {
$this->hyperlink = $hyperlink;
}
$this->php_Activity();
$this->Print_Widget();
}
}
class child extends HtmlObj
{
public $id;
protected function php_Activity()
{
return;
}
protected function print_Widget()
{
print $this->id;
}
function __construct($id)
{
$this->id = $id;
parent::__construct();
}
}
new child(10);