I want to create a function in a class that is available for a set of users, but that they won't be able to access. Ex:
class Stuff_for_user {
private $errors;
/*
* private $errors gets modified by private functions
*/
public function get_errors(){ // This is for users to display errors.
return $this->errors;
}
/*something here...*/ function set_errors($str){
$this->errors = $str;
}
}
So far so good, but now I want the parent class to be able to set Stuff_for_User's errors:
class Main_mess {
public index(){
$user_available_data = new Stuff_for_user();
if($big_error)
$user_available_data->set_errors("BIG ERROR!!!");
$this->send_to_users($user_available_data);
}
}
I want only Main_mess to be able to access Stuff_for_User's set_errors() method. Is that possible?
No, that is not possible like that, since Main_mess is not a parent class of Stuff_for_users (and this is probably what you want, looking at what your code actually does). So set_errors has to be public if you want to call it from the outside.
This is not possible how you want to implement it.
Some ideas (i dont know why or how you want to do that but just ideas...):
do set_error($str,$access_key) and let $access_key be an access string only you know!
let Stuff_for_user be in Extended_Stuff_for_user which has the set_error function like:
class Extended_Stuff_for_user {
private $errors;
private $Stuff_for_user;
public function set_errors() {
/* ... */
}
public function getStuffForUser() {
return $this->Stuff_for_user;
}
}
It seems that you are looking for implementation of something called friend class in php. Well .. i'm sorry to tell you this, but it is not possible.
You should look at other possible solutions to your problem.
class SecureContainer{
protected $user = null;
protected $target = null;
public function __construct( $target, $user )
{
$this->target = $target;
$this->user = $user;
}
public function __call( $method, $arguments )
{
if ( $this->user->isAllowed(getType( $this->target ), $method))
{
return call_user_func_array(
array( $this->target, $method), $arguments );
}
}
}
Use it like this:
$something = new UnsecureSomething;
$user = new User( $uid );
$something = new SecureContainer( $something, $user );
This should let you control the access to methods.
Yes it possible but it can be dirty.
Like This.
class Stuff_for_user {
private $errors;
/*
* private $errors gets modified by private functions
*/
public function get_errors(){ // This is for users to display errors.
return $this->errors;
}
/*
This way the child classes of Main will able be to use the set_errors function;
*/
function set_errors($class,$str){
if($class instanceof Main_mess)
{
$this->errors = $str;
}
/*
AndThis way the only Main_mess will be able;
*/
function set_errors($class,$str){
if(get_class($class)=="Main_mess")
{
$this->errors = $str;
}
}
class Main_mess {
public index(){
$user_available_data = new Stuff_for_user();
if($big_error)
$user_available_data->set_errors($this,"BIG ERROR!!!");
$this->send_to_users($user_available_data);
}
}
Related
I'm making a Laravel package, which is a basic API Wrapper to practice. I want my code completely re-usable and neat, well that's the reason we learn OOP I think :P
Let me first attach my code, and I'll explain what I'm trying to achieve via comments.
// This is how I'm calling my class
Shiprocket::
withCredential('other-than-default') // this is optional
->order(203504661) // pass order id
->details() // finally fetch the details
// This is my main class it's behind a Larvel Facade Accessor
class Shiprocket
{
protected $credentials;
protected $token;
// I'm using it as a constructor to initilize with a different credentil pair.
public function withCredential($credential_id)
{
$this->credentials = config('shiprocket.credentials')[$credential_id];
$this->token = $this->getToken();
return $this;
}
public function __construct()
{
$this->credentials = config('shiprocket.credentials')[config('shiprocket.default_credentials')];
$this->token = $this->getToken();
}
public function order($order_id = null)
{
return new OrderResource($order_id);
// Here my doubt starts
// I want to return another class (OrderResource) for Order related methods
// so that we can call Order related methods like:
// Shiprocket::withCredential('my-credential')->order()->getAll()
// and those methods will also use methods & properties of this Main class
// like the token, get(), post()
}
public function shipment($shipment_id = null)
{
return new ShipmentResource($shipment_id);
// and maybe I can also have more child classes like OrderResource
// So that I can call similar methods as OrderResource for shipments like ... ->getAll()
// or ... ->status()
// but these methods won't be reusable - they'll be completely different, just sometimes
// might have same names.
}
public function getToken(): string
{
$duration = config('shiprocket.token_cache') ? config('shiprocket.token_cache_duration') : 0;
return cache()->remember("shiprocket-{$this->credentials['email']}", $duration, function () {
return Http::post("https://apiv2.shiprocket.in/v1/external/auth/login", [
'email' => $this->credentials['email'],
'password' => $this->credentials['password'],
])->json()['token'];
});
}
public function get($url, $data = null)
{
return Http::withToken($this->token)->get($url, $data)->json();
}
public function post($url, $data = null)
{
return Http::withToken($this->token)->post($url, $data)->json();
}
}
It's okay even if you don't attach any code, maybe just guide me a bit what would be the best way to achieve something like this.
The chain methods that you want to apply it's called the Builder pattern
Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
you can learn and find snippets from here https://refactoring.guru/design-patterns/builder
back to your case, I cant agree that we need the builder pattern here, but let's try to have the small steps with your code, let's say you want to build Shiprocket object that contains the Order and the Shipment
the simple change you need is to return the Shiprocket so the code should look like this
<?php
class Shiprocket
{
protected $credentials;
protected $token;
private $order;
private $shipment;
public function withCredential($credential_id)
{
$this->credentials = config('shiprocket.credentials')[$credential_id];
$this->token = $this->getToken();
$this->order = null;
$this->shipment = null;
return $this;
}
public function __construct()
{
$this->credentials = config('shiprocket.credentials')[config('shiprocket.default_credentials')];
$this->token = $this->getToken();
$this->order = null;
$this->shipment = null;
}
public function order($order_id = null)
{
$this->order = new OrderResource($order_id);
return $this;
}
public function shipment($shipment_id = null)
{
$this->shipment = new ShipmentResource($shipment_id);
return $this;
}
public function getOrder(){
return $this->order;
}
public function getShipment(){
return $this->shipment;
}
public function getToken(): string
{
$duration = config('shiprocket.token_cache') ? config('shiprocket.token_cache_duration') : 0;
return cache()->remember("shiprocket-{$this->credentials['email']}", $duration, function () {
return Http::post("https://apiv2.shiprocket.in/v1/external/auth/login", [
'email' => $this->credentials['email'],
'password' => $this->credentials['password'],
])->json()['token'];
});
}
public function get($url, $data = null)
{
return Http::withToken($this->token)->get($url, $data)->json();
}
public function post($url, $data = null)
{
return Http::withToken($this->token)->post($url, $data)->json();
}
}
Note: the code could not be perfect when it comes to the standard and the best practice I just change it to follow your idea
I hope it's helpful
I am trying to display an array of messages at the end of my PHP class. My message handler is working, but only if I "add_message" from within the main parent class and not if I call this function from within a child class. Sorry if this is vague but was not sure how to word the question.
TLDR; How can I add a message from within class Example?
MAIN PARENT CLASS
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->messages->display_messages();
}
}
MESSAGE HANDLER
class Message_Handler {
public function __construct() {
$this->messages = array();
}
public function add_message( $msg ) {
$this->messages = $this->add( $this->messages, $msg );
}
private function add( $messages, $msg ) {
$messages[] = $msg;
return $messages;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( $this->messages );
}
}
EXAMPLE CLASS
class Example {
public function fire_instance() {
$this->messages = new Message_Handler();
$this->messages->add_message( 'Hello Universe!' ); // This message is NOT being displayed...
}
}
Because you want to keep the messages around different object, you should pass the object or use a static variable.
I would use a static variable like so:
class Init {
public function __construct() {
$this->load_dependencies();
$this->add_messages();
$this->add_msg_from_instance();
}
private function load_dependencies() {
require_once ROOT . 'classes/class-messages.php';
require_once ROOT . 'classes/class-example.php';
}
public function add_messages() {
// renamed the message handler variable for clarity
$this->message_handler = new Message_Handler();
$this->message_handler->add_message( 'hello world' );
}
// I Would like to add a message from within this instance....
public function add_msg_from_instance() {
$example = new Example();
$example->fire_instance();
}
public function run() {
$this->message_handler->display_messages();
}
}
class Message_Handler {
// use a static var to remember the messages over all objects
public static $_messages = array();
// add message to static
public function add_message( $msg ) {
self::$_messages[] = $msg;
}
// Final Function - Should display array of all messages
public function display_messages() {
var_dump( self::$_messages );
}
}
class Example {
public function fire_instance() {
// new object, same static array
$message_handler = new Message_Handler();
$message_handler->add_message( 'Hello Universe!' );
}
}
// testing...
new Init();
new Init();
$init = new Init();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->add_msg_from_instance();
$init->run();
Although global variables might not be the best design decision, you have at least two approaches to achieve what you want:
Use singleton.
Nowadays it is considered anti-pattern, but it is the simplest way: make message handler a singleton:
class MessageHandler
{
private static $instance;
private $messages = [];
public static function instance(): self
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
}
public function addMessage($message): self
{
$this->messages[] = $message;
return $this;
}
public function messages(): array
{
return $this->messages;
}
}
Then instead of creating a new instance of MessageHandler access it via the static method MessageHandler::instance(). Here is a demo.
Use DI container to inject the same instance (that is created once and held in the container) into all instances that need to access it. This approach is more preferable, but harder to implement in the project where there is no DI container available in the first place.
Is there a way to use a object variable instantiated from a class in two functions?
Here's the code I've tried, but its just returning null:
class bookAppointmentsController extends APIController
{
private $business;
public funcition check($key)
{
$this->business = new APIClass();
$setconnection = $this->business->connectAPI($key);
}
public function book()
{
dd($this->business) //returns null
$this->business->book();
}
}
I am trying to use the $business object in two functions but it does not work, when I dd($business) it returns null
Any way to do this?
Move the instantiation to the constructor:
public function __construct(APIClass $business)
{
$this->business = $business;
}
However, it would be better if you make Laravel do the heavy lifting and prepare the APIClass for you.
In your AppServicePorvider under the register method, you can create the APIClass
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind('APIClass', function ($app) {
$api = new APIClass();
// Do any logic required to prepare and check the api
$key = config('API_KEY');
$api->connectAPI($key);
return $api;
});
}
Check the documentations for more details.
Maybe the solution could be to make the variable Global
You could make the variable global:
function method( $args ) {
global $newVar;
$newVar = "Something";
}
function second_method() {
global $newVar;
echo $newVar;
}
Or you could return it from the first method and use it in the second method
public function check($key)
{
$this->business = new APIClass();
$setconnection = $this->business->connectAPI($key);
return $this->business;
}
public function book()
{
$business = check($key);
$business->book();
}
I have a php singleton session class as follows.
class Session {
static private $_instance = NULL;
private function __construct()
{
session_start();
}
/**
* Prevents the class from being cloned
* #return NULL
*/
private function __clone() { }
/**
* Returns the singleton instance of this class
* #return Session
*/
public static function getInstance()
{
if (!self::$_instance) {
self::$_instance = new Session();
}
return self::$_instance;
}
public function __get($key) {
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
}
return NULL;
}
public function __set($key, $value)
{
$_SESSION[$key] = $value;
}
public function __isset($key) {
return isset($_SESSION[$key]);
}
public function __unset($key) {
unset($_SESSION[$key]);
}
}
I can create an object as follows
$session = Session::getInstance();
$session->name = 'some name';
I can also get the value like
echo $session->name;
The problem is, i want to pass an array to this object and it is not working. for example, i wan to set something like
$_SESSION['user']['data'] = array('name'=>'some name',"empId"=>'123');
I am trying like this.
$session->['user']['data'] = array('name'=>'some name',"empId"=>'123');
but it is not working. Could you please suggest what is wrong.
The workaround in this case would be to use:
public function &__get($key) {
if (isset($_SESSION[$key])) {
return & $_SESSION[$key];
}
return NULL;
}
You need to modify the __get() method, because an assignment like
$session->user['data'] = ...
will actually retrieve the [user] key, and then try to assign a new subarray [data] to that temporary array result.
Also note that $session->['user']['data'] is invalid syntax. You either need $session->user['data'] or $session->{'user'}['data'].
Anyway, I think it is probably not a good idea to use a wrapper if you often want to do assignments like that. (I do actually have something very similar.)
$session->user = array('data' => array('name'=>'some name',"empId"=>'123'));
Make sure you don't overwrite anything else in user you want to keep
In a lot of my PHP classes, I have this code:
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
The point is so that outside code can know if an object has an error state, what the string of the error is, etc. But to have this exact code in a bunch of different classes is repetitious!
I'd love to have a dual-extension where I could do
class childClass extends parentClass, error {
...
}
And have those properties and methods inborn, But PHP doesn't support multiple inheritances. What I'm thinking about doing is creating an error class that exists inside each class. If I make it public, I can call it directly through the object
if ( $myObject->error->isError() ) {...}
but wouldn't that also make its error status settable from outside the containing class,
$myObject->error->setError("I shouldn't be doing this here");
which I would rather avoid?
Or I could write 'gateway' functions in the containing class, which do the appropriate calls on the error object, and prevent setting the error status from outside,
class childClass extends parentClass {
private $error;
public function __construct(...) {
...
$error = & new error();
...
}
public function isError() {...}
public function getError() {...}
public function getErrorCode() {...}
private function setError() {...}
...
}
but that leads to (some of) the code duplication that I'm trying to avoid.
What's the optimal solution here? I'm trying to have functionality for error statuses for a number of objects, so that the outside world can see their error state, with minimal repetition.
Use composition instead of inheritance.
class Errors {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
And now use a private instance variable to refer to it:
class childClass extends parentClass {
private $errors = new Errors();
...
}
The private visibility prevents you from referencing $errors outside of the class.
There's also no need to create isError(), getError(), etc. inside childClass (and therefore no need to worry about code duplication). Simply call $this->errors->isError(), $this->errors->getError(), etc. If you still wanted to require those methods to be implemented though, as suggested below, you could specify an interface.
You could also abuse the __call magic method to do the same thing:
public function __call($name, array $arguments) {
$name = strtolower($name);
if (isset($this->methods[$name])) {
array_unshift($arguments, $this);
return call_user_func_array($this->methods[$name], $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
Note that I said abuse... Ideally, I'd think of a different architecture rather than having all these "common methods" everywhere. Why not use an exception instead of checking $foo->isError? If that's not appropriate, why not decorate a class?
class Errors
protected $object = null;
public function __construct($object) {
$this->object = $object;
}
public function __call($method, array $arguments) {
$callback = array($this->object, $method);
if (is_callable($callback)) {
return call_user_func_array($callback, $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
public function __get($name) { return $this->object->$name; }
public function __set($name, $value) { $this->object->$name = $value; }
// Your methods here
public function isInstance($name) { return $this->object instanceof $name; }
}
Then just "wrap" your existing object in that class:
$obj = new Errors($obj);
$obj->foo();
As of PHP 5.4, you can use Traits.
For example you could make Trait called ErrorTrait like this:
trait ErrorTrait {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
Then you would define your child class like this:
class childClass extends parentClass {
use ErrorTrait;
...
}
Traits work basically like copy/paste so all of the code in the trait would be available within the class (without the code duplication).